init
This commit is contained in:
230
.gitignore
vendored
Normal file
230
.gitignore
vendored
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
build/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
|
||||||
|
# Visual Studio 2015 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
wwwroot/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUNIT
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# DNX
|
||||||
|
project.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_i.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# JustCode is a .NET coding add-in
|
||||||
|
.JustCode
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/packages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/packages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/packages/repositories.config
|
||||||
|
|
||||||
|
# Windows Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Windows Azure Emulator
|
||||||
|
efc/
|
||||||
|
rfc/
|
||||||
|
|
||||||
|
# Windows Store app package directory
|
||||||
|
AppPackages/
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
[Ss]tyle[Cc]op.*
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
node_modules/
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
8
NuGet.Config
Normal file
8
NuGet.Config
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<packageSources>
|
||||||
|
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
|
||||||
|
<clear />
|
||||||
|
<add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||||
|
</packageSources>
|
||||||
|
</configuration>
|
||||||
15
Readme.md
Normal file
15
Readme.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
Add [turbolink](https://github.com/rails/turbolinks) support to ASP.NET 5 applications
|
||||||
|
|
||||||
|
This provides middlewear to add support for turbolinks. Simply add `app.UseTurboLinks();` and a build of turbolinks (one can be found in the wwwroot/js dir of the example project)
|
||||||
|
|
||||||
|
|
||||||
|
## Why use turbolinks?
|
||||||
|
|
||||||
|
If you have an application that may not fit into an SPA, or just have a lot of code that is tied to .NET this provides SPA like speed by ajaxing the html and replacing it. This allows the browser to keep the cache of existing scripts. Turbolinks was made in the rails community, and a lot of existing documentation already exists.
|
||||||
|
|
||||||
|
|
||||||
|
**Warning** This stops page loads thus `$(document).ready(function(){})` does not fire on new pages.
|
||||||
|
|
||||||
|
## What about asp.net 4x?
|
||||||
|
|
||||||
|
I am going to back port this to owin, and eventually more classic version of ASP.NET In the mean time, you can find an MVC action filter [here](https://github.com/kazimanzurrashid/aspnetmvcturbolinks)
|
||||||
40
TurboLinks.Net.sln
Normal file
40
TurboLinks.Net.sln
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 14
|
||||||
|
VisualStudioVersion = 14.0.23107.0
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{505F6C3D-2B40-442B-ABBB-69275B465571}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{15647589-E22B-47B8-B149-6693C35A09F2}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
global.json = global.json
|
||||||
|
NuGet.Config = NuGet.Config
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "TurboLinks.Net", "src\TurboLinks.Net\TurboLinks.Net.xproj", "{B57EEE3B-37FF-49B5-8841-84B759DF1471}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "TurboLinks.Net.Example", "src\TurboLinks.Net.Example\TurboLinks.Net.Example.xproj", "{B2ADC3D3-2EE1-4662-9130-FC2DFAFE2BBF}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{B57EEE3B-37FF-49B5-8841-84B759DF1471}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B57EEE3B-37FF-49B5-8841-84B759DF1471}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B57EEE3B-37FF-49B5-8841-84B759DF1471}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B57EEE3B-37FF-49B5-8841-84B759DF1471}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B2ADC3D3-2EE1-4662-9130-FC2DFAFE2BBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B2ADC3D3-2EE1-4662-9130-FC2DFAFE2BBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B2ADC3D3-2EE1-4662-9130-FC2DFAFE2BBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B2ADC3D3-2EE1-4662-9130-FC2DFAFE2BBF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(NestedProjects) = preSolution
|
||||||
|
{B57EEE3B-37FF-49B5-8841-84B759DF1471} = {505F6C3D-2B40-442B-ABBB-69275B465571}
|
||||||
|
{B2ADC3D3-2EE1-4662-9130-FC2DFAFE2BBF} = {505F6C3D-2B40-442B-ABBB-69275B465571}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
6
global.json
Normal file
6
global.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"projects": [ "src", "test" ],
|
||||||
|
"sdk": {
|
||||||
|
"version": "1.0.0-beta8"
|
||||||
|
}
|
||||||
|
}
|
||||||
3
src/TurboLinks.Net.Example/.bowerrc
Normal file
3
src/TurboLinks.Net.Example/.bowerrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"directory": "wwwroot/lib"
|
||||||
|
}
|
||||||
477
src/TurboLinks.Net.Example/Controllers/AccountController.cs
Normal file
477
src/TurboLinks.Net.Example/Controllers/AccountController.cs
Normal file
@@ -0,0 +1,477 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Authorization;
|
||||||
|
using Microsoft.AspNet.Identity;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
|
using Microsoft.Data.Entity;
|
||||||
|
using TurboLinks.Net.Example.Models;
|
||||||
|
using TurboLinks.Net.Example.Services;
|
||||||
|
using TurboLinks.Net.Example.ViewModels.Account;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.Controllers
|
||||||
|
{
|
||||||
|
[Authorize]
|
||||||
|
public class AccountController : Controller
|
||||||
|
{
|
||||||
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||||
|
private readonly IEmailSender _emailSender;
|
||||||
|
private readonly ISmsSender _smsSender;
|
||||||
|
private readonly ApplicationDbContext _applicationDbContext;
|
||||||
|
private static bool _databaseChecked;
|
||||||
|
|
||||||
|
public AccountController(
|
||||||
|
UserManager<ApplicationUser> userManager,
|
||||||
|
SignInManager<ApplicationUser> signInManager,
|
||||||
|
IEmailSender emailSender,
|
||||||
|
ISmsSender smsSender,
|
||||||
|
ApplicationDbContext applicationDbContext)
|
||||||
|
{
|
||||||
|
_userManager = userManager;
|
||||||
|
_signInManager = signInManager;
|
||||||
|
_emailSender = emailSender;
|
||||||
|
_smsSender = smsSender;
|
||||||
|
_applicationDbContext = applicationDbContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/Login
|
||||||
|
[HttpGet]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public IActionResult Login(string returnUrl = null)
|
||||||
|
{
|
||||||
|
ViewData["ReturnUrl"] = returnUrl;
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Account/Login
|
||||||
|
[HttpPost]
|
||||||
|
[AllowAnonymous]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
|
||||||
|
{
|
||||||
|
EnsureDatabaseCreated(_applicationDbContext);
|
||||||
|
ViewData["ReturnUrl"] = returnUrl;
|
||||||
|
if (ModelState.IsValid)
|
||||||
|
{
|
||||||
|
// This doesn't count login failures towards account lockout
|
||||||
|
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
|
||||||
|
var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
|
||||||
|
if (result.Succeeded)
|
||||||
|
{
|
||||||
|
return RedirectToLocal(returnUrl);
|
||||||
|
}
|
||||||
|
if (result.RequiresTwoFactor)
|
||||||
|
{
|
||||||
|
return RedirectToAction(nameof(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<IActionResult> Register(RegisterViewModel model)
|
||||||
|
{
|
||||||
|
EnsureDatabaseCreated(_applicationDbContext);
|
||||||
|
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: HttpContext.Request.Scheme);
|
||||||
|
//await _emailSender.SendEmailAsync(model.Email, "Confirm your account",
|
||||||
|
// "Please confirm your account by clicking this link: <a href=\"" + callbackUrl + "\">link</a>");
|
||||||
|
await _signInManager.SignInAsync(user, isPersistent: false);
|
||||||
|
return RedirectToAction(nameof(HomeController.Index), "Home");
|
||||||
|
}
|
||||||
|
AddErrors(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got this far, something failed, redisplay form
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Account/LogOff
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> LogOff()
|
||||||
|
{
|
||||||
|
await _signInManager.SignOutAsync();
|
||||||
|
return RedirectToAction(nameof(HomeController.Index), "Home");
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Account/ExternalLogin
|
||||||
|
[HttpPost]
|
||||||
|
[AllowAnonymous]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public IActionResult ExternalLogin(string provider, string returnUrl = null)
|
||||||
|
{
|
||||||
|
EnsureDatabaseCreated(_applicationDbContext);
|
||||||
|
// 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<IActionResult> ExternalLoginCallback(string returnUrl = null)
|
||||||
|
{
|
||||||
|
var info = await _signInManager.GetExternalLoginInfoAsync();
|
||||||
|
if (info == null)
|
||||||
|
{
|
||||||
|
return RedirectToAction(nameof(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(nameof(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.
|
||||||
|
ViewData["ReturnUrl"] = returnUrl;
|
||||||
|
ViewData["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<IActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl = null)
|
||||||
|
{
|
||||||
|
if (User.IsSignedIn())
|
||||||
|
{
|
||||||
|
return RedirectToAction(nameof(ManageController.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewData["ReturnUrl"] = returnUrl;
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET: /Account/ConfirmEmail
|
||||||
|
[HttpGet]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<IActionResult> 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<IActionResult> 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: HttpContext.Request.Scheme);
|
||||||
|
//await _emailSender.SendEmailAsync(model.Email, "Reset Password",
|
||||||
|
// "Please reset your password by clicking here: <a href=\"" + callbackUrl + "\">link</a>");
|
||||||
|
//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<IActionResult> 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(nameof(AccountController.ResetPasswordConfirmation), "Account");
|
||||||
|
}
|
||||||
|
var result = await _userManager.ResetPasswordAsync(user, model.Code, model.Password);
|
||||||
|
if (result.Succeeded)
|
||||||
|
{
|
||||||
|
return RedirectToAction(nameof(AccountController.ResetPasswordConfirmation), "Account");
|
||||||
|
}
|
||||||
|
AddErrors(result);
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/ResetPasswordConfirmation
|
||||||
|
[HttpGet]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public IActionResult ResetPasswordConfirmation()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/SendCode
|
||||||
|
[HttpGet]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<ActionResult> 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<IActionResult> 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 _emailSender.SendEmailAsync(await _userManager.GetEmailAsync(user), "Security Code", message);
|
||||||
|
}
|
||||||
|
else if (model.SelectedProvider == "Phone")
|
||||||
|
{
|
||||||
|
await _smsSender.SendSmsAsync(await _userManager.GetPhoneNumberAsync(user), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return RedirectToAction(nameof(VerifyCode), new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe });
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/VerifyCode
|
||||||
|
[HttpGet]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<IActionResult> 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<IActionResult> 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
|
||||||
|
|
||||||
|
// The following code creates the database and schema if they don't exist.
|
||||||
|
// This is a temporary workaround since deploying database through EF migrations is
|
||||||
|
// not yet supported in this release.
|
||||||
|
// Please see this http://go.microsoft.com/fwlink/?LinkID=615859 for more information on how to do deploy the database
|
||||||
|
// when publishing your application.
|
||||||
|
private static void EnsureDatabaseCreated(ApplicationDbContext context)
|
||||||
|
{
|
||||||
|
if (!_databaseChecked)
|
||||||
|
{
|
||||||
|
_databaseChecked = true;
|
||||||
|
context.Database.Migrate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddErrors(IdentityResult result)
|
||||||
|
{
|
||||||
|
foreach (var error in result.Errors)
|
||||||
|
{
|
||||||
|
ModelState.AddModelError(string.Empty, error.Description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ApplicationUser> GetCurrentUserAsync()
|
||||||
|
{
|
||||||
|
return await _userManager.FindByIdAsync(HttpContext.User.GetUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private IActionResult RedirectToLocal(string returnUrl)
|
||||||
|
{
|
||||||
|
if (Url.IsLocalUrl(returnUrl))
|
||||||
|
{
|
||||||
|
return Redirect(returnUrl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return RedirectToAction(nameof(HomeController.Index), "Home");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/TurboLinks.Net.Example/Controllers/HomeController.cs
Normal file
33
src/TurboLinks.Net.Example/Controllers/HomeController.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.Controllers
|
||||||
|
{
|
||||||
|
public class HomeController : Controller
|
||||||
|
{
|
||||||
|
public IActionResult Index()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void About()
|
||||||
|
{
|
||||||
|
Response.Redirect("http://about.tommyparnell.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult Contact()
|
||||||
|
{
|
||||||
|
ViewData["Message"] = "Your contact page.";
|
||||||
|
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult Error()
|
||||||
|
{
|
||||||
|
return View("~/Views/Shared/Error.cshtml");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
373
src/TurboLinks.Net.Example/Controllers/ManageController.cs
Normal file
373
src/TurboLinks.Net.Example/Controllers/ManageController.cs
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using Microsoft.AspNet.Authorization;
|
||||||
|
using Microsoft.AspNet.Identity;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using TurboLinks.Net.Example.Models;
|
||||||
|
using TurboLinks.Net.Example.Services;
|
||||||
|
using TurboLinks.Net.Example.ViewModels.Manage;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.Controllers
|
||||||
|
{
|
||||||
|
[Authorize]
|
||||||
|
public class ManageController : Controller
|
||||||
|
{
|
||||||
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||||
|
private readonly IEmailSender _emailSender;
|
||||||
|
private readonly ISmsSender _smsSender;
|
||||||
|
|
||||||
|
public ManageController(
|
||||||
|
UserManager<ApplicationUser> userManager,
|
||||||
|
SignInManager<ApplicationUser> signInManager,
|
||||||
|
IEmailSender emailSender,
|
||||||
|
ISmsSender smsSender)
|
||||||
|
{
|
||||||
|
_userManager = userManager;
|
||||||
|
_signInManager = signInManager;
|
||||||
|
_emailSender = emailSender;
|
||||||
|
_smsSender = smsSender;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/Index
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> Index(ManageMessageId? message = null)
|
||||||
|
{
|
||||||
|
ViewData["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<IActionResult> RemoveLogin()
|
||||||
|
{
|
||||||
|
var user = await GetCurrentUserAsync();
|
||||||
|
var linkedAccounts = await _userManager.GetLoginsAsync(user);
|
||||||
|
ViewData["ShowRemoveButton"] = await _userManager.HasPasswordAsync(user) || linkedAccounts.Count > 1;
|
||||||
|
return View(linkedAccounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Manage/RemoveLogin
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> 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(nameof(ManageLogins), new { Message = message });
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/AddPhoneNumber
|
||||||
|
public IActionResult AddPhoneNumber()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Account/AddPhoneNumber
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> 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.PhoneNumber);
|
||||||
|
await _smsSender.SendSmsAsync(model.PhoneNumber, "Your security code is: " + code);
|
||||||
|
return RedirectToAction(nameof(VerifyPhoneNumber), new { PhoneNumber = model.PhoneNumber });
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Manage/EnableTwoFactorAuthentication
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> EnableTwoFactorAuthentication()
|
||||||
|
{
|
||||||
|
var user = await GetCurrentUserAsync();
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
await _userManager.SetTwoFactorEnabledAsync(user, true);
|
||||||
|
await _signInManager.SignInAsync(user, isPersistent: false);
|
||||||
|
}
|
||||||
|
return RedirectToAction(nameof(Index), "Manage");
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Manage/DisableTwoFactorAuthentication
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> DisableTwoFactorAuthentication()
|
||||||
|
{
|
||||||
|
var user = await GetCurrentUserAsync();
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
await _userManager.SetTwoFactorEnabledAsync(user, false);
|
||||||
|
await _signInManager.SignInAsync(user, isPersistent: false);
|
||||||
|
}
|
||||||
|
return RedirectToAction(nameof(Index), "Manage");
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/VerifyPhoneNumber
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> 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<IActionResult> 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(nameof(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<IActionResult> 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(nameof(Index), new { Message = ManageMessageId.RemovePhoneSuccess });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error });
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Manage/ChangePassword
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult ChangePassword()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Account/Manage
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> 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(nameof(Index), new { Message = ManageMessageId.ChangePasswordSuccess });
|
||||||
|
}
|
||||||
|
AddErrors(result);
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error });
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Manage/SetPassword
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult SetPassword()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Manage/SetPassword
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> 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(nameof(Index), new { Message = ManageMessageId.SetPasswordSuccess });
|
||||||
|
}
|
||||||
|
AddErrors(result);
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error });
|
||||||
|
}
|
||||||
|
|
||||||
|
//GET: /Account/Manage
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> ManageLogins(ManageMessageId? message = null)
|
||||||
|
{
|
||||||
|
ViewData["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();
|
||||||
|
ViewData["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<ActionResult> LinkLoginCallback()
|
||||||
|
{
|
||||||
|
var user = await GetCurrentUserAsync();
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return View("Error");
|
||||||
|
}
|
||||||
|
var info = await _signInManager.GetExternalLoginInfoAsync(User.GetUserId());
|
||||||
|
if (info == null)
|
||||||
|
{
|
||||||
|
return RedirectToAction(nameof(ManageLogins), new { Message = ManageMessageId.Error });
|
||||||
|
}
|
||||||
|
var result = await _userManager.AddLoginAsync(user, info);
|
||||||
|
var message = result.Succeeded ? ManageMessageId.AddLoginSuccess : ManageMessageId.Error;
|
||||||
|
return RedirectToAction(nameof(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<bool> 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<ApplicationUser> GetCurrentUserAsync()
|
||||||
|
{
|
||||||
|
return await _userManager.FindByIdAsync(HttpContext.User.GetUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private IActionResult RedirectToLocal(string returnUrl)
|
||||||
|
{
|
||||||
|
if (Url.IsLocalUrl(returnUrl))
|
||||||
|
{
|
||||||
|
return Redirect(returnUrl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return RedirectToAction(nameof(HomeController.Index), nameof(HomeController));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
183
src/TurboLinks.Net.Example/Migrations/00000000000000_CreateIdentitySchema.Designer.cs
generated
Normal file
183
src/TurboLinks.Net.Example/Migrations/00000000000000_CreateIdentitySchema.Designer.cs
generated
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Data.Entity;
|
||||||
|
using Microsoft.Data.Entity.Infrastructure;
|
||||||
|
using Microsoft.Data.Entity.Metadata;
|
||||||
|
using Microsoft.Data.Entity.Migrations;
|
||||||
|
using TurboLinks.Net.Example.Models;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
[Migration("00000000000000_CreateIdentitySchema")]
|
||||||
|
partial class CreateIdentitySchema
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder
|
||||||
|
.Annotation("ProductVersion", "7.0.0-beta8")
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.Annotation("MaxLength", 256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.Annotation("MaxLength", 256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.Index("NormalizedName")
|
||||||
|
.Annotation("Relational:Name", "RoleNameIndex");
|
||||||
|
|
||||||
|
b.Annotation("Relational:TableName", "AspNetRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.Annotation("Relational:TableName", "AspNetRoleClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.Annotation("Relational:TableName", "AspNetUserClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName");
|
||||||
|
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.Annotation("Relational:TableName", "AspNetUserLogins");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.Annotation("Relational:TableName", "AspNetUserRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TurboLinks.Net.Example.Models.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id");
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.Annotation("MaxLength", 256);
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.Annotation("MaxLength", 256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.Annotation("MaxLength", 256);
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.Annotation("MaxLength", 256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.Index("NormalizedEmail")
|
||||||
|
.Annotation("Relational:Name", "EmailIndex");
|
||||||
|
|
||||||
|
b.Index("NormalizedUserName")
|
||||||
|
.Annotation("Relational:Name", "UserNameIndex");
|
||||||
|
|
||||||
|
b.Annotation("Relational:TableName", "AspNetUsers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNet.Identity.EntityFramework.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.ForeignKey("RoleId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("TurboLinks.Net.Example.Models.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.ForeignKey("UserId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("TurboLinks.Net.Example.Models.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.ForeignKey("UserId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNet.Identity.EntityFramework.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.ForeignKey("RoleId");
|
||||||
|
|
||||||
|
b.HasOne("TurboLinks.Net.Example.Models.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.ForeignKey("UserId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Data.Entity.Metadata;
|
||||||
|
using Microsoft.Data.Entity.Migrations;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.Migrations
|
||||||
|
{
|
||||||
|
public partial class CreateIdentitySchema : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetRoles",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(nullable: false),
|
||||||
|
ConcurrencyStamp = table.Column<string>(nullable: true),
|
||||||
|
Name = table.Column<string>(nullable: true),
|
||||||
|
NormalizedName = table.Column<string>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_IdentityRole", x => x.Id);
|
||||||
|
});
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUsers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(nullable: false),
|
||||||
|
AccessFailedCount = table.Column<int>(nullable: false),
|
||||||
|
ConcurrencyStamp = table.Column<string>(nullable: true),
|
||||||
|
Email = table.Column<string>(nullable: true),
|
||||||
|
EmailConfirmed = table.Column<bool>(nullable: false),
|
||||||
|
LockoutEnabled = table.Column<bool>(nullable: false),
|
||||||
|
LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
|
||||||
|
NormalizedEmail = table.Column<string>(nullable: true),
|
||||||
|
NormalizedUserName = table.Column<string>(nullable: true),
|
||||||
|
PasswordHash = table.Column<string>(nullable: true),
|
||||||
|
PhoneNumber = table.Column<string>(nullable: true),
|
||||||
|
PhoneNumberConfirmed = table.Column<bool>(nullable: false),
|
||||||
|
SecurityStamp = table.Column<string>(nullable: true),
|
||||||
|
TwoFactorEnabled = table.Column<bool>(nullable: false),
|
||||||
|
UserName = table.Column<string>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ApplicationUser", x => x.Id);
|
||||||
|
});
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetRoleClaims",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
ClaimType = table.Column<string>(nullable: true),
|
||||||
|
ClaimValue = table.Column<string>(nullable: true),
|
||||||
|
RoleId = table.Column<string>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_IdentityRoleClaim<string>", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_IdentityRoleClaim<string>_IdentityRole_RoleId",
|
||||||
|
column: x => x.RoleId,
|
||||||
|
principalTable: "AspNetRoles",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUserClaims",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
ClaimType = table.Column<string>(nullable: true),
|
||||||
|
ClaimValue = table.Column<string>(nullable: true),
|
||||||
|
UserId = table.Column<string>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_IdentityUserClaim<string>", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_IdentityUserClaim<string>_ApplicationUser_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUserLogins",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
LoginProvider = table.Column<string>(nullable: false),
|
||||||
|
ProviderKey = table.Column<string>(nullable: false),
|
||||||
|
ProviderDisplayName = table.Column<string>(nullable: true),
|
||||||
|
UserId = table.Column<string>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_IdentityUserLogin<string>", x => new { x.LoginProvider, x.ProviderKey });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_IdentityUserLogin<string>_ApplicationUser_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUserRoles",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
UserId = table.Column<string>(nullable: false),
|
||||||
|
RoleId = table.Column<string>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_IdentityUserRole<string>", x => new { x.UserId, x.RoleId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_IdentityUserRole<string>_IdentityRole_RoleId",
|
||||||
|
column: x => x.RoleId,
|
||||||
|
principalTable: "AspNetRoles",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_IdentityUserRole<string>_ApplicationUser_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "RoleNameIndex",
|
||||||
|
table: "AspNetRoles",
|
||||||
|
column: "NormalizedName");
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "EmailIndex",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
column: "NormalizedEmail");
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "UserNameIndex",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
column: "NormalizedUserName");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable("AspNetRoleClaims");
|
||||||
|
migrationBuilder.DropTable("AspNetUserClaims");
|
||||||
|
migrationBuilder.DropTable("AspNetUserLogins");
|
||||||
|
migrationBuilder.DropTable("AspNetUserRoles");
|
||||||
|
migrationBuilder.DropTable("AspNetRoles");
|
||||||
|
migrationBuilder.DropTable("AspNetUsers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Data.Entity;
|
||||||
|
using Microsoft.Data.Entity.Infrastructure;
|
||||||
|
using Microsoft.Data.Entity.Metadata;
|
||||||
|
using Microsoft.Data.Entity.Migrations;
|
||||||
|
using TurboLinks.Net.Example.Models;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder
|
||||||
|
.Annotation("ProductVersion", "7.0.0-beta8")
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.Annotation("MaxLength", 256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.Annotation("MaxLength", 256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.Index("NormalizedName")
|
||||||
|
.Annotation("Relational:Name", "RoleNameIndex");
|
||||||
|
|
||||||
|
b.Annotation("Relational:TableName", "AspNetRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.Annotation("Relational:TableName", "AspNetRoleClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.Annotation("Relational:TableName", "AspNetUserClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName");
|
||||||
|
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.Annotation("Relational:TableName", "AspNetUserLogins");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.Annotation("Relational:TableName", "AspNetUserRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TurboLinks.Net.Example.Models.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id");
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.Annotation("MaxLength", 256);
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.Annotation("MaxLength", 256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.Annotation("MaxLength", 256);
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.Annotation("MaxLength", 256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.Index("NormalizedEmail")
|
||||||
|
.Annotation("Relational:Name", "EmailIndex");
|
||||||
|
|
||||||
|
b.Index("NormalizedUserName")
|
||||||
|
.Annotation("Relational:Name", "UserNameIndex");
|
||||||
|
|
||||||
|
b.Annotation("Relational:TableName", "AspNetUsers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNet.Identity.EntityFramework.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.ForeignKey("RoleId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("TurboLinks.Net.Example.Models.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.ForeignKey("UserId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("TurboLinks.Net.Example.Models.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.ForeignKey("UserId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNet.Identity.EntityFramework.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.ForeignKey("RoleId");
|
||||||
|
|
||||||
|
b.HasOne("TurboLinks.Net.Example.Models.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.ForeignKey("UserId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/TurboLinks.Net.Example/Models/ApplicationDbContext.cs
Normal file
20
src/TurboLinks.Net.Example/Models/ApplicationDbContext.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Identity.EntityFramework;
|
||||||
|
using Microsoft.Data.Entity;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.Models
|
||||||
|
{
|
||||||
|
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/TurboLinks.Net.Example/Models/ApplicationUser.cs
Normal file
13
src/TurboLinks.Net.Example/Models/ApplicationUser.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Identity.EntityFramework;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.Models
|
||||||
|
{
|
||||||
|
// Add profile data for application users by adding properties to the ApplicationUser class
|
||||||
|
public class ApplicationUser : IdentityUser
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/TurboLinks.Net.Example/Properties/AssemblyInfo.cs
Normal file
23
src/TurboLinks.Net.Example/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("TurboLinks.Net.Example")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("TurboLinks.Net.Example")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2015")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("b2adc3d3-2ee1-4662-9130-fc2dfafe2bbf")]
|
||||||
12
src/TurboLinks.Net.Example/Services/IEmailSender.cs
Normal file
12
src/TurboLinks.Net.Example/Services/IEmailSender.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.Services
|
||||||
|
{
|
||||||
|
public interface IEmailSender
|
||||||
|
{
|
||||||
|
Task SendEmailAsync(string email, string subject, string message);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/TurboLinks.Net.Example/Services/ISmsSender.cs
Normal file
12
src/TurboLinks.Net.Example/Services/ISmsSender.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.Services
|
||||||
|
{
|
||||||
|
public interface ISmsSender
|
||||||
|
{
|
||||||
|
Task SendSmsAsync(string number, string message);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/TurboLinks.Net.Example/Services/MessageServices.cs
Normal file
25
src/TurboLinks.Net.Example/Services/MessageServices.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.Services
|
||||||
|
{
|
||||||
|
// 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 class AuthMessageSender : IEmailSender, ISmsSender
|
||||||
|
{
|
||||||
|
public Task SendEmailAsync(string email, string subject, string message)
|
||||||
|
{
|
||||||
|
// Plug in your email service here to send an email.
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task SendSmsAsync(string number, string message)
|
||||||
|
{
|
||||||
|
// Plug in your SMS service here to send a text message.
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
141
src/TurboLinks.Net.Example/Startup.cs
Normal file
141
src/TurboLinks.Net.Example/Startup.cs
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
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.Entity;
|
||||||
|
using Microsoft.AspNet.Hosting;
|
||||||
|
using Microsoft.AspNet.Identity.EntityFramework;
|
||||||
|
using Microsoft.Data.Entity;
|
||||||
|
using Microsoft.Dnx.Runtime;
|
||||||
|
using Microsoft.Framework.Configuration;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.Logging;
|
||||||
|
using TurboLinks.Net.Example.Models;
|
||||||
|
using TurboLinks.Net.Example.Services;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
|
||||||
|
{
|
||||||
|
// Setup configuration sources.
|
||||||
|
|
||||||
|
var builder = new ConfigurationBuilder()
|
||||||
|
.SetBasePath(appEnv.ApplicationBasePath)
|
||||||
|
.AddJsonFile("appsettings.json")
|
||||||
|
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
|
||||||
|
|
||||||
|
if(env.IsDevelopment())
|
||||||
|
{
|
||||||
|
// 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
|
||||||
|
builder.AddUserSecrets();
|
||||||
|
}
|
||||||
|
builder.AddEnvironmentVariables();
|
||||||
|
Configuration = builder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IConfigurationRoot 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 Entity Framework services to the services container.
|
||||||
|
services.AddEntityFramework()
|
||||||
|
.AddSqlServer()
|
||||||
|
.AddDbContext<ApplicationDbContext>(options =>
|
||||||
|
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
|
||||||
|
|
||||||
|
// Add Identity services to the services container.
|
||||||
|
services.AddIdentity<ApplicationUser, IdentityRole>()
|
||||||
|
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||||
|
.AddDefaultTokenProviders();
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
// Register application services.
|
||||||
|
services.AddTransient<IEmailSender, AuthMessageSender>();
|
||||||
|
services.AddTransient<ISmsSender, AuthMessageSender>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure is called after ConfigureServices is called.
|
||||||
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
||||||
|
{
|
||||||
|
loggerFactory.MinimumLevel = LogLevel.Information;
|
||||||
|
loggerFactory.AddConsole();
|
||||||
|
loggerFactory.AddDebug();
|
||||||
|
|
||||||
|
// Configure the HTTP request pipeline.
|
||||||
|
|
||||||
|
// Add the following to the request pipeline only in development environment.
|
||||||
|
if(env.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseBrowserLink();
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
|
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.UseExceptionHandler("/Home/Error");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the platform handler to the request pipeline.
|
||||||
|
app.UseIISPlatformHandler();
|
||||||
|
|
||||||
|
// Add static files to the request pipeline.
|
||||||
|
app.UseStaticFiles();
|
||||||
|
app.UseTurboLinks();
|
||||||
|
|
||||||
|
// Add cookie-based authentication to the request pipeline.
|
||||||
|
app.UseIdentity();
|
||||||
|
|
||||||
|
// Add and configure the options for authentication middleware to the request pipeline.
|
||||||
|
// You can add options for middleware as shown below.
|
||||||
|
// For more information see http://go.microsoft.com/fwlink/?LinkID=532715
|
||||||
|
//app.UseFacebookAuthentication(options =>
|
||||||
|
//{
|
||||||
|
// options.AppId = Configuration["Authentication:Facebook:AppId"];
|
||||||
|
// options.AppSecret = Configuration["Authentication:Facebook:AppSecret"];
|
||||||
|
//});
|
||||||
|
//app.UseGoogleAuthentication(options =>
|
||||||
|
//{
|
||||||
|
// options.ClientId = Configuration["Authentication:Google:ClientId"];
|
||||||
|
// options.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
|
||||||
|
//});
|
||||||
|
//app.UseMicrosoftAccountAuthentication(options =>
|
||||||
|
//{
|
||||||
|
// options.ClientId = Configuration["Authentication:MicrosoftAccount:ClientId"];
|
||||||
|
// options.ClientSecret = Configuration["Authentication:MicrosoftAccount:ClientSecret"];
|
||||||
|
//});
|
||||||
|
//app.UseTwitterAuthentication(options =>
|
||||||
|
//{
|
||||||
|
// options.ConsumerKey = Configuration["Authentication:Twitter:ConsumerKey"];
|
||||||
|
// options.ConsumerSecret = Configuration["Authentication:Twitter:ConsumerSecret"];
|
||||||
|
//});
|
||||||
|
|
||||||
|
// Add MVC to the request pipeline.
|
||||||
|
app.UseMvc(routes =>
|
||||||
|
{
|
||||||
|
routes.MapRoute(
|
||||||
|
name: "default",
|
||||||
|
template: "{controller=Home}/{action=Index}/{id?}");
|
||||||
|
|
||||||
|
// Uncomment the following line to add a route for porting Web API 2 controllers.
|
||||||
|
// routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/TurboLinks.Net.Example/TurboLinks.Net.Example.xproj
Normal file
19
src/TurboLinks.Net.Example/TurboLinks.Net.Example.xproj
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>b2adc3d3-2ee1-4662-9130-fc2dfafe2bbf</ProjectGuid>
|
||||||
|
<RootNamespace>TurboLinks.Net.Example</RootNamespace>
|
||||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<DevelopmentServerPort>59553</DevelopmentServerPort>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.ViewModels.Account
|
||||||
|
{
|
||||||
|
public class ExternalLoginConfirmationViewModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[EmailAddress]
|
||||||
|
public string Email { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.ViewModels.Account
|
||||||
|
{
|
||||||
|
public class ForgotPasswordViewModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[EmailAddress]
|
||||||
|
public string Email { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.ViewModels.Account
|
||||||
|
{
|
||||||
|
public class LoginViewModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[EmailAddress]
|
||||||
|
public string Email { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
public string Password { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Remember me?")]
|
||||||
|
public bool RememberMe { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.ViewModels.Account
|
||||||
|
{
|
||||||
|
public class RegisterViewModel
|
||||||
|
{
|
||||||
|
[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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.ViewModels.Account
|
||||||
|
{
|
||||||
|
public class ResetPasswordViewModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[EmailAddress]
|
||||||
|
public string Email { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
||||||
|
[DataType(DataType.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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.ViewModels.Account
|
||||||
|
{
|
||||||
|
public class SendCodeViewModel
|
||||||
|
{
|
||||||
|
public string SelectedProvider { get; set; }
|
||||||
|
|
||||||
|
public ICollection<SelectListItem> Providers { get; set; }
|
||||||
|
|
||||||
|
public string ReturnUrl { get; set; }
|
||||||
|
|
||||||
|
public bool RememberMe { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.ViewModels.Account
|
||||||
|
{
|
||||||
|
public class VerifyCodeViewModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string Provider { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string Code { get; set; }
|
||||||
|
|
||||||
|
public string ReturnUrl { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Remember this browser?")]
|
||||||
|
public bool RememberBrowser { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Remember me?")]
|
||||||
|
public bool RememberMe { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.ViewModels.Manage
|
||||||
|
{
|
||||||
|
public class AddPhoneNumberViewModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[Phone]
|
||||||
|
[Display(Name = "Phone number")]
|
||||||
|
public string PhoneNumber { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.ViewModels.Manage
|
||||||
|
{
|
||||||
|
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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.ViewModels.Manage
|
||||||
|
{
|
||||||
|
public class ConfigureTwoFactorViewModel
|
||||||
|
{
|
||||||
|
public string SelectedProvider { get; set; }
|
||||||
|
|
||||||
|
public ICollection<SelectListItem> Providers { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.ViewModels.Manage
|
||||||
|
{
|
||||||
|
public class FactorViewModel
|
||||||
|
{
|
||||||
|
public string Purpose { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Identity;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.ViewModels.Manage
|
||||||
|
{
|
||||||
|
public class IndexViewModel
|
||||||
|
{
|
||||||
|
public bool HasPassword { get; set; }
|
||||||
|
|
||||||
|
public IList<UserLoginInfo> Logins { get; set; }
|
||||||
|
|
||||||
|
public string PhoneNumber { get; set; }
|
||||||
|
|
||||||
|
public bool TwoFactor { get; set; }
|
||||||
|
|
||||||
|
public bool BrowserRemembered { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Http.Authentication;
|
||||||
|
using Microsoft.AspNet.Identity;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.ViewModels.Manage
|
||||||
|
{
|
||||||
|
public class ManageLoginsViewModel
|
||||||
|
{
|
||||||
|
public IList<UserLoginInfo> CurrentLogins { get; set; }
|
||||||
|
|
||||||
|
public IList<AuthenticationDescription> OtherLogins { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.ViewModels.Manage
|
||||||
|
{
|
||||||
|
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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net.Example.ViewModels.Manage
|
||||||
|
{
|
||||||
|
public class VerifyPhoneNumberViewModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string Code { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[Phone]
|
||||||
|
[Display(Name = "Phone number")]
|
||||||
|
public string PhoneNumber { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/TurboLinks.Net.Example/Views/Account/ConfirmEmail.cshtml
Normal file
10
src/TurboLinks.Net.Example/Views/Account/ConfirmEmail.cshtml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
@{
|
||||||
|
ViewData["Title"] = "Confirm Email";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewData["Title"].</h2>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Thank you for confirming your email. Please <a asp-controller="Account" asp-action="Login">Click here to Log in</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
@model ExternalLoginConfirmationViewModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Register";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewData["Title"].</h2>
|
||||||
|
<h3>Associate your @ViewData["LoginProvider"] account.</h3>
|
||||||
|
|
||||||
|
<form asp-controller="Account" asp-action="ExternalLoginConfirmation" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal" role="form">
|
||||||
|
<h4>Association Form</h4>
|
||||||
|
<hr />
|
||||||
|
<div asp-validation-summary="ValidationSummary.All" class="text-danger"></div>
|
||||||
|
|
||||||
|
<p class="text-info">
|
||||||
|
You've successfully authenticated with <strong>@ViewData["LoginProvider"]</strong>.
|
||||||
|
Please enter a user name for this site below and click the Register button to finish
|
||||||
|
logging in.
|
||||||
|
</p>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Email" class="col-md-2 control-label"></label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<input asp-for="Email" class="form-control" />
|
||||||
|
<span asp-validation-for="Email" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<button type="submit" class="btn btn-default">Register</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
@{
|
||||||
|
ViewData["Title"] = "Login Failure";
|
||||||
|
}
|
||||||
|
|
||||||
|
<hgroup>
|
||||||
|
<h2>@ViewData["Title"].</h2>
|
||||||
|
<h3 class="text-danger">Unsuccessful login with service.</h3>
|
||||||
|
</hgroup>
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
@model ForgotPasswordViewModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Forgot your password?";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewData["Title"].</h2>
|
||||||
|
<p>
|
||||||
|
For more information on how to enable reset password please see this <a href="http://go.microsoft.com/fwlink/?LinkID=532713">article</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
@*<form asp-controller="Account" asp-action="ForgotPassword" method="post" class="form-horizontal" role="form">
|
||||||
|
<h4>Enter your email.</h4>
|
||||||
|
<hr />
|
||||||
|
<div asp-validation-summary="ValidationSummary.All" class="text-danger"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Email" class="col-md-2 control-label"></label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<input asp-for="Email" class="form-control" />
|
||||||
|
<span asp-validation-for="Email" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<button type="submit" class="btn btn-default">Submit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>*@
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
@{
|
||||||
|
ViewData["Title"] = "Forgot Password Confirmation";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewData["Title"].</h2>
|
||||||
|
<p>
|
||||||
|
Please check your email to reset your password.
|
||||||
|
</p>
|
||||||
8
src/TurboLinks.Net.Example/Views/Account/Lockout.cshtml
Normal file
8
src/TurboLinks.Net.Example/Views/Account/Lockout.cshtml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
@{
|
||||||
|
ViewData["Title"] = "Locked out";
|
||||||
|
}
|
||||||
|
|
||||||
|
<hgroup>
|
||||||
|
<h1 class="text-danger">Locked out.</h1>
|
||||||
|
<h2 class="text-danger">This account has been locked out, please try again later.</h2>
|
||||||
|
</hgroup>
|
||||||
90
src/TurboLinks.Net.Example/Views/Account/Login.cshtml
Normal file
90
src/TurboLinks.Net.Example/Views/Account/Login.cshtml
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
@using System.Collections.Generic
|
||||||
|
@using Microsoft.AspNet.Http
|
||||||
|
@using Microsoft.AspNet.Http.Authentication
|
||||||
|
@model LoginViewModel
|
||||||
|
@inject SignInManager<ApplicationUser> SignInManager
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Log in";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewData["Title"].</h2>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<section>
|
||||||
|
<form asp-controller="Account" asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal" role="form">
|
||||||
|
<h4>Use a local account to log in.</h4>
|
||||||
|
<hr />
|
||||||
|
<div asp-validation-summary="ValidationSummary.All" class="text-danger"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Email" class="col-md-2 control-label"></label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<input asp-for="Email" class="form-control" />
|
||||||
|
<span asp-validation-for="Email" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Password" class="col-md-2 control-label"></label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<input asp-for="Password" class="form-control" />
|
||||||
|
<span asp-validation-for="Password" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<div class="checkbox">
|
||||||
|
<input asp-for="RememberMe" />
|
||||||
|
<label asp-for="RememberMe"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<button type="submit" class="btn btn-default">Log in</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
<a asp-action="Register">Register as a new user?</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a asp-action="ForgotPassword">Forgot your password?</a>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<section>
|
||||||
|
<h4>Use another service to log in.</h4>
|
||||||
|
<hr />
|
||||||
|
@{
|
||||||
|
var loginProviders = SignInManager.GetExternalAuthenticationSchemes().ToList();
|
||||||
|
if (loginProviders.Count == 0)
|
||||||
|
{
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
There are no external authentication services configured. See <a href="http://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
|
||||||
|
for details on setting up this ASP.NET application to support logging in via external services.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<form asp-controller="Account" asp-action="ExternalLogin" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal" role="form">
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
@foreach (var provider in loginProviders)
|
||||||
|
{
|
||||||
|
<button type="submit" class="btn btn-default" name="provider" value="@provider.AuthenticationScheme" title="Log in using your @provider.DisplayName account">@provider.AuthenticationScheme</button>
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||||
|
}
|
||||||
42
src/TurboLinks.Net.Example/Views/Account/Register.cshtml
Normal file
42
src/TurboLinks.Net.Example/Views/Account/Register.cshtml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
@model RegisterViewModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Register";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewData["Title"].</h2>
|
||||||
|
|
||||||
|
<form asp-controller="Account" asp-action="Register" method="post" class="form-horizontal" role="form">
|
||||||
|
<h4>Create a new account.</h4>
|
||||||
|
<hr />
|
||||||
|
<div asp-validation-summary="ValidationSummary.All" class="text-danger"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Email" class="col-md-2 control-label"></label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<input asp-for="Email" class="form-control" />
|
||||||
|
<span asp-validation-for="Email" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Password" class="col-md-2 control-label"></label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<input asp-for="Password" class="form-control" />
|
||||||
|
<span asp-validation-for="Password" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="ConfirmPassword" class="col-md-2 control-label"></label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<input asp-for="ConfirmPassword" class="form-control" />
|
||||||
|
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<button type="submit" class="btn btn-default">Register</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
@model ResetPasswordViewModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Reset password";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewData["Title"].</h2>
|
||||||
|
|
||||||
|
<form asp-controller="Account" asp-action="ResetPassword" method="post" class="form-horizontal" role="form">
|
||||||
|
<h4>Reset your password.</h4>
|
||||||
|
<hr />
|
||||||
|
<div asp-validation-summary="ValidationSummary.All" class="text-danger"></div>
|
||||||
|
<input asp-for="Code" type="hidden" />
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Email" class="col-md-2 control-label"></label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<input asp-for="Email" class="form-control" />
|
||||||
|
<span asp-validation-for="Email" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Password" class="col-md-2 control-label"></label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<input asp-for="Password" class="form-control" />
|
||||||
|
<span asp-validation-for="Password" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="ConfirmPassword" class="col-md-2 control-label"></label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<input asp-for="ConfirmPassword" class="form-control" />
|
||||||
|
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<button type="submit" class="btn btn-default">Reset</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
@{
|
||||||
|
ViewData["Title"] = "Reset password confirmation";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h1>@ViewData["Title"].</h1>
|
||||||
|
<p>
|
||||||
|
Your password has been reset. Please <a asp-controller="Account" asp-action="Login">Click here to log in</a>.
|
||||||
|
</p>
|
||||||
21
src/TurboLinks.Net.Example/Views/Account/SendCode.cshtml
Normal file
21
src/TurboLinks.Net.Example/Views/Account/SendCode.cshtml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
@model SendCodeViewModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Send Verification Code";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewData["Title"].</h2>
|
||||||
|
|
||||||
|
<form asp-controller="Account" asp-action="SendCode" asp-route-returnurl="@Model.ReturnUrl" method="post" class="form-horizontal" role="form">
|
||||||
|
<input asp-for="RememberMe" type="hidden" />
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
|
Select Two-Factor Authentication Provider:
|
||||||
|
<select asp-for="SelectedProvider" asp-items="Model.Providers"></select>
|
||||||
|
<button type="submit" class="btn btn-default">Submit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||||
|
}
|
||||||
38
src/TurboLinks.Net.Example/Views/Account/VerifyCode.cshtml
Normal file
38
src/TurboLinks.Net.Example/Views/Account/VerifyCode.cshtml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
@model VerifyCodeViewModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Verify";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewData["Title"].</h2>
|
||||||
|
|
||||||
|
<form asp-controller="Account" asp-action="VerifyCode" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal" role="form">
|
||||||
|
<div asp-validation-summary="ValidationSummary.All" class="text-danger"></div>
|
||||||
|
<input asp-for="Provider" type="hidden" />
|
||||||
|
<input asp-for="RememberMe" type="hidden" />
|
||||||
|
<h4>@ViewData["Status"]</h4>
|
||||||
|
<hr />
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Code" class="col-md-2 control-label"></label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<input asp-for="Code" class="form-control" />
|
||||||
|
<span asp-validation-for="Code" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<div class="checkbox">
|
||||||
|
<input asp-for="RememberBrowser" />
|
||||||
|
<label asp-for="RememberBrowser"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<button type="submit" class="btn btn-default">Submit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||||
|
}
|
||||||
7
src/TurboLinks.Net.Example/Views/Home/About.cshtml
Normal file
7
src/TurboLinks.Net.Example/Views/Home/About.cshtml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
@{
|
||||||
|
ViewData["Title"] = "About";
|
||||||
|
}
|
||||||
|
<h2>@ViewData["Title"].</h2>
|
||||||
|
<h3>@ViewData["Message"]</h3>
|
||||||
|
|
||||||
|
<p>Use this area to provide additional information.</p>
|
||||||
17
src/TurboLinks.Net.Example/Views/Home/Contact.cshtml
Normal file
17
src/TurboLinks.Net.Example/Views/Home/Contact.cshtml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
@{
|
||||||
|
ViewData["Title"] = "Contact";
|
||||||
|
}
|
||||||
|
<h2>@ViewData["Title"].</h2>
|
||||||
|
<h3>@ViewData["Message"]</h3>
|
||||||
|
|
||||||
|
<address>
|
||||||
|
One Microsoft Way<br />
|
||||||
|
Redmond, WA 98052-6399<br />
|
||||||
|
<abbr title="Phone">P:</abbr>
|
||||||
|
425.555.0100
|
||||||
|
</address>
|
||||||
|
|
||||||
|
<address>
|
||||||
|
<strong>Support:</strong> <a href="mailto:Support@example.com">Support@example.com</a><br />
|
||||||
|
<strong>Marketing:</strong> <a href="mailto:Marketing@example.com">Marketing@example.com</a>
|
||||||
|
</address>
|
||||||
104
src/TurboLinks.Net.Example/Views/Home/Index.cshtml
Normal file
104
src/TurboLinks.Net.Example/Views/Home/Index.cshtml
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
@{
|
||||||
|
ViewData["Title"] = "Home Page";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div id="myCarousel" class="carousel slide" data-ride="carousel" data-interval="6000">
|
||||||
|
<ol class="carousel-indicators">
|
||||||
|
<li data-target="#myCarousel" data-slide-to="0" class="active"></li>
|
||||||
|
<li data-target="#myCarousel" data-slide-to="1"></li>
|
||||||
|
<li data-target="#myCarousel" data-slide-to="2"></li>
|
||||||
|
<li data-target="#myCarousel" data-slide-to="3"></li>
|
||||||
|
</ol>
|
||||||
|
<div class="carousel-inner" role="listbox">
|
||||||
|
<div class="item active">
|
||||||
|
<img src="~/images/ASP-NET-Banners-01.png" alt="ASP.NET" class="img-responsive" />
|
||||||
|
<div class="container">
|
||||||
|
<div class="carousel-caption">
|
||||||
|
<p>
|
||||||
|
Learn how to build ASP.NET apps that can run anywhere.
|
||||||
|
<a class="btn btn-default btn-default" href="http://go.microsoft.com/fwlink/?LinkID=525028&clcid=0x409">
|
||||||
|
Learn More
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<img src="~/images/Banner-02-VS.png" alt="Visual Studio" class="img-responsive" />
|
||||||
|
<div class="container">
|
||||||
|
<div class="carousel-caption">
|
||||||
|
<p>
|
||||||
|
There are powerful new features in Visual Studio for building modern web apps.
|
||||||
|
<a class="btn btn-default btn-default" href="http://go.microsoft.com/fwlink/?LinkID=525030&clcid=0x409">
|
||||||
|
Learn More
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<img src="~/images/ASP-NET-Banners-02.png" alt="Package Management" class="img-responsive" />
|
||||||
|
<div class="container">
|
||||||
|
<div class="carousel-caption">
|
||||||
|
<p>
|
||||||
|
Bring in libraries from NuGet, Bower, and npm, and automate tasks using Grunt or Gulp.
|
||||||
|
<a class="btn btn-default btn-default" href="http://go.microsoft.com/fwlink/?LinkID=525029&clcid=0x409">
|
||||||
|
Learn More
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<img src="~/images/Banner-01-Azure.png" alt="Microsoft Azure" class="img-responsive" />
|
||||||
|
<div class="container">
|
||||||
|
<div class="carousel-caption">
|
||||||
|
<p>
|
||||||
|
Learn how Microsoft's Azure cloud platform allows you to build, deploy, and scale web apps.
|
||||||
|
<a class="btn btn-default btn-default" href="http://go.microsoft.com/fwlink/?LinkID=525027&clcid=0x409">
|
||||||
|
Learn More
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<h2>Application uses</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Sample pages using ASP.NET 5 (MVC 6)</li>
|
||||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkId=518007">Gulp</a> and <a href="http://go.microsoft.com/fwlink/?LinkId=518004">Bower</a> for managing client-side resources</li>
|
||||||
|
<li>Theming using <a href="http://go.microsoft.com/fwlink/?LinkID=398939">Bootstrap</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<h2>New concepts</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkId=518008">Conceptual overview of ASP.NET 5</a></li>
|
||||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkId=518008">Fundamentals in ASP.NET 5</a></li>
|
||||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkID=517849">Client-Side Development using npm, Bower and Gulp</a></li>
|
||||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkID=517850">Develop on different platforms</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<h2>Customize app</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkID=398600">Add Controllers and Views</a></li>
|
||||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkID=398602">Add Data using EntityFramework</a></li>
|
||||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkID=398603">Add Authentication using Identity</a></li>
|
||||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkID=517848">Manage client-side packages using Bower/ Gulp</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<h2>Deploy</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkID=517851">Run your app locally</a></li>
|
||||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkID=517852">Run your app on .NET Core</a></li>
|
||||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkID=517853">Run commands in your app</a></li>
|
||||||
|
<li><a href="http://go.microsoft.com/fwlink/?LinkID=398609">Publish to Microsoft Azure Web Apps</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
@model AddPhoneNumberViewModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Add Phone Number";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewData["Title"].</h2>
|
||||||
|
<form asp-controller="Manage" asp-action="AddPhoneNumber" method="post" class="form-horizontal" role="form">
|
||||||
|
<h4>Add a phone number.</h4>
|
||||||
|
<hr />
|
||||||
|
<div asp-validation-summary="ValidationSummary.All" class="text-danger"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="PhoneNumber" class="col-md-2 control-label"></label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<input asp-for="PhoneNumber" class="form-control" />
|
||||||
|
<span asp-validation-for="PhoneNumber" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<button type="submit" class="btn btn-default">Send verification code</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
@model ChangePasswordViewModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Change Password";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewData["Title"].</h2>
|
||||||
|
|
||||||
|
<form asp-controller="Manage" asp-action="ChangePassword" method="post" class="form-horizontal" role="form">
|
||||||
|
<h4>Change Password Form</h4>
|
||||||
|
<hr />
|
||||||
|
<div asp-validation-summary="ValidationSummary.All" class="text-danger"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="OldPassword" class="col-md-2 control-label"></label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<input asp-for="OldPassword" class="form-control" />
|
||||||
|
<span asp-validation-for="OldPassword" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="NewPassword" class="col-md-2 control-label"></label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<input asp-for="NewPassword" class="form-control" />
|
||||||
|
<span asp-validation-for="NewPassword" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="ConfirmPassword" class="col-md-2 control-label"></label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<input asp-for="ConfirmPassword" class="form-control" />
|
||||||
|
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<button type="submit" class="btn btn-default">Change password</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||||
|
}
|
||||||
79
src/TurboLinks.Net.Example/Views/Manage/Index.cshtml
Normal file
79
src/TurboLinks.Net.Example/Views/Manage/Index.cshtml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
@model IndexViewModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Manage your account";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewData["Title"].</h2>
|
||||||
|
<p class="text-success">@ViewData["StatusMessage"]</p>
|
||||||
|
<div>
|
||||||
|
<h4>Change your account settings</h4>
|
||||||
|
<hr />
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
<dt>Password:</dt>
|
||||||
|
<dd>
|
||||||
|
[
|
||||||
|
@if (Model.HasPassword)
|
||||||
|
{
|
||||||
|
<a asp-controller="Manage" asp-action="ChangePassword">Change</a>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<a asp-controller="Manage" asp-action="SetPassword">Create</a>
|
||||||
|
}
|
||||||
|
]
|
||||||
|
</dd>
|
||||||
|
<dt>External Logins:</dt>
|
||||||
|
<dd>
|
||||||
|
|
||||||
|
@Model.Logins.Count [<a asp-controller="Manage" asp-action="ManageLogins">Manage</a>]
|
||||||
|
</dd>
|
||||||
|
|
||||||
|
|
||||||
|
<dt>Phone Number:</dt>
|
||||||
|
<dd>
|
||||||
|
<p>
|
||||||
|
Phone Numbers can used as a second factor of verification in two-factor authentication.
|
||||||
|
See <a href="http://go.microsoft.com/fwlink/?LinkID=532713">this article</a>
|
||||||
|
for details on setting up this ASP.NET application to support two-factor authentication using SMS.
|
||||||
|
</p>
|
||||||
|
@*@(Model.PhoneNumber ?? "None") [
|
||||||
|
@if (Model.PhoneNumber != null)
|
||||||
|
{
|
||||||
|
<a asp-controller="Manage" asp-action="AddPhoneNumber">Change</a>
|
||||||
|
@: |
|
||||||
|
<a asp-controller="Manage" asp-action="RemovePhoneNumber">Remove</a>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<a asp-controller="Manage" asp-action="AddPhoneNumber">Add</a>
|
||||||
|
}
|
||||||
|
]*@
|
||||||
|
</dd>
|
||||||
|
|
||||||
|
<dt>Two-Factor Authentication:</dt>
|
||||||
|
<dd>
|
||||||
|
<p>
|
||||||
|
There are no two-factor authentication providers configured. See <a href="http://go.microsoft.com/fwlink/?LinkID=532713">this article</a>
|
||||||
|
for setting up this application to support two-factor authentication.
|
||||||
|
</p>
|
||||||
|
@*@if (Model.TwoFactor)
|
||||||
|
{
|
||||||
|
<form asp-controller="Manage" asp-action="DisableTwoFactorAuthentication" method="post" class="form-horizontal" role="form">
|
||||||
|
<text>
|
||||||
|
Enabled
|
||||||
|
<button type="submit" class="btn btn-link">Disable</button>
|
||||||
|
</text>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<form asp-controller="Manage" asp-action="EnableTwoFactorAuthentication" method="post" class="form-horizontal" role="form">
|
||||||
|
<text>
|
||||||
|
Disabled
|
||||||
|
<button type="submit" class="btn btn-link">Enable</button>
|
||||||
|
</text>
|
||||||
|
</form>
|
||||||
|
}*@
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
54
src/TurboLinks.Net.Example/Views/Manage/ManageLogins.cshtml
Normal file
54
src/TurboLinks.Net.Example/Views/Manage/ManageLogins.cshtml
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
@model ManageLoginsViewModel
|
||||||
|
@using Microsoft.AspNet.Http.Authentication
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Manage your external logins";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewData["Title"].</h2>
|
||||||
|
|
||||||
|
<p class="text-success">@ViewData["StatusMessage"]</p>
|
||||||
|
@if (Model.CurrentLogins.Count > 0)
|
||||||
|
{
|
||||||
|
<h4>Registered Logins</h4>
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
@foreach (var account in Model.CurrentLogins)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>@account.LoginProvider</td>
|
||||||
|
<td>
|
||||||
|
@if ((bool)ViewData["ShowRemoveButton"])
|
||||||
|
{
|
||||||
|
<form asp-controller="Manage" asp-action="RemoveLogin" method="post" class="form-horizontal" role="form">
|
||||||
|
<div>
|
||||||
|
<input asp-for="@account.LoginProvider" type="hidden" />
|
||||||
|
<input asp-for="@account.ProviderKey" type="hidden" />
|
||||||
|
<input type="submit" class="btn btn-default" value="Remove" title="Remove this @account.LoginProvider login from your account" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@:
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
@if (Model.OtherLogins.Count > 0)
|
||||||
|
{
|
||||||
|
<h4>Add another service to log in.</h4>
|
||||||
|
<hr />
|
||||||
|
<form asp-controller="Manage" asp-action="LinkLogin" method="post" class="form-horizontal" role="form">
|
||||||
|
<div id="socialLoginList">
|
||||||
|
<p>
|
||||||
|
@foreach (var provider in Model.OtherLogins)
|
||||||
|
{
|
||||||
|
<button type="submit" class="btn btn-default" name="provider" value="@provider.AuthenticationScheme" title="Log in using your @provider.DisplayName account">@provider.AuthenticationScheme</button>
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
35
src/TurboLinks.Net.Example/Views/Manage/RemoveLogin.cshtml
Normal file
35
src/TurboLinks.Net.Example/Views/Manage/RemoveLogin.cshtml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
@model ICollection<Microsoft.AspNet.Identity.UserLoginInfo>
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Remove Login";
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (Model.Count > 0)
|
||||||
|
{
|
||||||
|
<h4>Registered Logins</h4>
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
@foreach (var account in Model)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>@account.LoginProvider</td>
|
||||||
|
<td>
|
||||||
|
@if ((bool)ViewData["ShowRemoveButton"])
|
||||||
|
{
|
||||||
|
<form asp-controller="Manage" asp-action="RemoveLogin" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal" role="form">
|
||||||
|
<div>
|
||||||
|
<input asp-for="@account.LoginProvider" type="hidden" />
|
||||||
|
<input asp-for="@account.ProviderKey" type="hidden" />
|
||||||
|
<input type="submit" class="btn btn-default" value="Remove" title="Remove this @account.LoginProvider login from your account" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@:
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
38
src/TurboLinks.Net.Example/Views/Manage/SetPassword.cshtml
Normal file
38
src/TurboLinks.Net.Example/Views/Manage/SetPassword.cshtml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
@model SetPasswordViewModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Set Password";
|
||||||
|
}
|
||||||
|
|
||||||
|
<p class="text-info">
|
||||||
|
You do not have a local username/password for this site. Add a local
|
||||||
|
account so you can log in without an external login.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form asp-controller="Manage" asp-action="SetPassword" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal" role="form">
|
||||||
|
<h4>Set your password</h4>
|
||||||
|
<hr />
|
||||||
|
<div asp-validation-summary="ValidationSummary.All" class="text-danger"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="NewPassword" class="col-md-2 control-label"></label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<input asp-for="NewPassword" class="form-control" />
|
||||||
|
<span asp-validation-for="NewPassword" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="ConfirmPassword" class="col-md-2 control-label"></label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<input asp-for="ConfirmPassword" class="form-control" />
|
||||||
|
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<button type="submit" class="btn btn-default">Set password</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
@model VerifyPhoneNumberViewModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Verify Phone Number";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewData["Title"].</h2>
|
||||||
|
|
||||||
|
<form asp-controller="Manage" asp-action="VerifyPhoneNumber" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal" role="form">
|
||||||
|
<input asp-for="PhoneNumber" type="hidden" />
|
||||||
|
<h4>Add a phone number.</h4>
|
||||||
|
<h5>@ViewData["Status"]</h5>
|
||||||
|
<hr />
|
||||||
|
<div asp-validation-summary="ValidationSummary.All" class="text-danger"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Code" class="col-md-2 control-label"></label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<input asp-for="Code" class="form-control" />
|
||||||
|
<span asp-validation-for="Code" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<button type="submit" class="btn btn-default">Submit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||||
|
}
|
||||||
6
src/TurboLinks.Net.Example/Views/Shared/Error.cshtml
Normal file
6
src/TurboLinks.Net.Example/Views/Shared/Error.cshtml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
@{
|
||||||
|
ViewData["Title"] = "Error";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h1 class="text-danger">Error.</h1>
|
||||||
|
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||||
61
src/TurboLinks.Net.Example/Views/Shared/_Layout.cshtml
Normal file
61
src/TurboLinks.Net.Example/Views/Shared/_Layout.cshtml
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>@ViewData["Title"] - TurboLinks.Net.Example</title>
|
||||||
|
|
||||||
|
<environment names="Development">
|
||||||
|
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
|
||||||
|
<link rel="stylesheet" href="~/lib/bootstrap-touch-carousel/dist/css/bootstrap-touch-carousel.css" />
|
||||||
|
<link rel="stylesheet" href="~/css/site.css" />
|
||||||
|
</environment>
|
||||||
|
<environment names="Staging,Production">
|
||||||
|
<link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/css/bootstrap.min.css"
|
||||||
|
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
|
||||||
|
asp-fallback-test-class="hidden" asp-fallback-test-property="visibility" asp-fallback-test-value="hidden" />
|
||||||
|
<link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap-touch-carousel/0.8.0/css/bootstrap-touch-carousel.css"
|
||||||
|
asp-fallback-href="~/lib/bootstrap-touch-carousel/css/bootstrap-touch-carousel.css"
|
||||||
|
asp-fallback-test-class="carousel-caption" asp-fallback-test-property="display" asp-fallback-test-value="none" />
|
||||||
|
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
|
||||||
|
</environment>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-header">
|
||||||
|
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
</button>
|
||||||
|
<a asp-controller="Home" asp-action="Index" class="navbar-brand">TurboLinks.Net.Example</a>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-collapse collapse">
|
||||||
|
<ul class="nav navbar-nav">
|
||||||
|
<li><a asp-controller="Home" asp-action="Index">Home</a></li>
|
||||||
|
<li><a asp-controller="Home" asp-action="About">About</a></li>
|
||||||
|
<li><a asp-controller="Home" asp-action="Contact">Contact</a></li>
|
||||||
|
</ul>
|
||||||
|
@await Html.PartialAsync("_LoginPartial")
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container body-content">
|
||||||
|
@RenderBody()
|
||||||
|
<hr />
|
||||||
|
<script src="~/lib/jquery/dist/jquery.js"></script>
|
||||||
|
<script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
|
||||||
|
<script src="~/lib/hammer.js/hammer.js"></script>
|
||||||
|
<script src="~/lib/bootstrap-touch-carousel/dist/js/bootstrap-touch-carousel.js"></script>
|
||||||
|
<script src="~/js/site.js"></script>
|
||||||
|
<script src="~/js/turbolinks.js"></script>
|
||||||
|
<footer>
|
||||||
|
<p>© 2015 - TurboLinks.Net.Example</p>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
@RenderSection("scripts", required: false)
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
20
src/TurboLinks.Net.Example/Views/Shared/_LoginPartial.cshtml
Normal file
20
src/TurboLinks.Net.Example/Views/Shared/_LoginPartial.cshtml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
@using System.Security.Claims
|
||||||
|
|
||||||
|
@if (User.Identity.IsAuthenticated)
|
||||||
|
{
|
||||||
|
<form asp-controller="Account" asp-action="LogOff" method="post" id="logoutForm" class="navbar-right">
|
||||||
|
<ul class="nav navbar-nav navbar-right">
|
||||||
|
<li>
|
||||||
|
<a asp-controller="Manage" asp-action="Index" title="Manage">Hello @User.GetUserName()!</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="javascript:document.getElementById('logoutForm').submit()">Log off</a></li>
|
||||||
|
</ul>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<ul class="nav navbar-nav navbar-right">
|
||||||
|
<li><a asp-controller="Account" asp-action="Register">Register</a></li>
|
||||||
|
<li><a asp-controller="Account" asp-action="Login">Log in</a></li>
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<environment names="Development">
|
||||||
|
<script src="~/lib/jquery-validation/jquery.validate.js"></script>
|
||||||
|
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
|
||||||
|
</environment>
|
||||||
|
<environment names="Staging,Production">
|
||||||
|
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.min.js"
|
||||||
|
asp-fallback-src="~/lib/jquery-validation/jquery.validate.js"
|
||||||
|
asp-fallback-test="window.jquery && window.jquery.validator">
|
||||||
|
</script>
|
||||||
|
<script src="https://ajax.aspnetcdn.com/ajax/mvc/5.2.3/jquery.validate.unobtrusive.min.js"
|
||||||
|
asp-fallback-src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
|
||||||
|
asp-fallback-test="window.jquery && window.jquery.validator && window.jquery.validator.unobtrusive">
|
||||||
|
</script>
|
||||||
|
</environment>
|
||||||
6
src/TurboLinks.Net.Example/Views/_ViewImports.cshtml
Normal file
6
src/TurboLinks.Net.Example/Views/_ViewImports.cshtml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
@using TurboLinks.Net.Example
|
||||||
|
@using TurboLinks.Net.Example.Models
|
||||||
|
@using TurboLinks.Net.Example.ViewModels.Account
|
||||||
|
@using TurboLinks.Net.Example.ViewModels.Manage
|
||||||
|
@using Microsoft.AspNet.Identity
|
||||||
|
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
|
||||||
3
src/TurboLinks.Net.Example/Views/_ViewStart.cshtml
Normal file
3
src/TurboLinks.Net.Example/Views/_ViewStart.cshtml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
@{
|
||||||
|
Layout = "_Layout";
|
||||||
|
}
|
||||||
7
src/TurboLinks.Net.Example/appsettings.json
Normal file
7
src/TurboLinks.Net.Example/appsettings.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"Data": {
|
||||||
|
"DefaultConnection": {
|
||||||
|
"ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=aspnet5-TurboLinks.Net.Example-660c79c7-b99e-4a1f-a454-505230c45a5e;Trusted_Connection=True;MultipleActiveResultSets=true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/TurboLinks.Net.Example/bower.json
Normal file
12
src/TurboLinks.Net.Example/bower.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "ASP.NET",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"bootstrap": "3.0.0",
|
||||||
|
"bootstrap-touch-carousel": "0.8.0",
|
||||||
|
"hammer.js": "2.0.4",
|
||||||
|
"jquery": "2.1.4",
|
||||||
|
"jquery-validation": "1.11.1",
|
||||||
|
"jquery-validation-unobtrusive": "3.2.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/TurboLinks.Net.Example/gulpfile.js
Normal file
45
src/TurboLinks.Net.Example/gulpfile.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/// <binding Clean='clean' />
|
||||||
|
|
||||||
|
var gulp = require("gulp"),
|
||||||
|
rimraf = require("rimraf"),
|
||||||
|
concat = require("gulp-concat"),
|
||||||
|
cssmin = require("gulp-cssmin"),
|
||||||
|
uglify = require("gulp-uglify"),
|
||||||
|
project = require("./project.json");
|
||||||
|
|
||||||
|
var paths = {
|
||||||
|
webroot: "./" + project.webroot + "/"
|
||||||
|
};
|
||||||
|
|
||||||
|
paths.js = paths.webroot + "js/**/*.js";
|
||||||
|
paths.minJs = paths.webroot + "js/**/*.min.js";
|
||||||
|
paths.css = paths.webroot + "css/**/*.css";
|
||||||
|
paths.minCss = paths.webroot + "css/**/*.min.css";
|
||||||
|
paths.concatJsDest = paths.webroot + "js/site.min.js";
|
||||||
|
paths.concatCssDest = paths.webroot + "css/site.min.css";
|
||||||
|
|
||||||
|
gulp.task("clean:js", function (cb) {
|
||||||
|
rimraf(paths.concatJsDest, cb);
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("clean:css", function (cb) {
|
||||||
|
rimraf(paths.concatCssDest, cb);
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("clean", ["clean:js", "clean:css"]);
|
||||||
|
|
||||||
|
gulp.task("min:js", function () {
|
||||||
|
gulp.src([paths.js, "!" + paths.minJs], { base: "." })
|
||||||
|
.pipe(concat(paths.concatJsDest))
|
||||||
|
.pipe(uglify())
|
||||||
|
.pipe(gulp.dest("."));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("min:css", function () {
|
||||||
|
gulp.src([paths.css, "!" + paths.minCss])
|
||||||
|
.pipe(concat(paths.concatCssDest))
|
||||||
|
.pipe(cssmin())
|
||||||
|
.pipe(gulp.dest("."));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("min", ["min:js", "min:css"]);
|
||||||
11
src/TurboLinks.Net.Example/package.json
Normal file
11
src/TurboLinks.Net.Example/package.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "ASP.NET",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"gulp": "3.8.11",
|
||||||
|
"gulp-concat": "2.5.2",
|
||||||
|
"gulp-cssmin": "0.1.7",
|
||||||
|
"gulp-uglify": "1.2.0",
|
||||||
|
"rimraf": "2.2.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
54
src/TurboLinks.Net.Example/project.json
Normal file
54
src/TurboLinks.Net.Example/project.json
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"webroot": "wwwroot",
|
||||||
|
"userSecretsId": "aspnet5-TurboLinks.Net.Example-660c79c7-b99e-4a1f-a454-505230c45a5e",
|
||||||
|
"version": "1.0.0-*",
|
||||||
|
|
||||||
|
"dependencies": {
|
||||||
|
"EntityFramework.Commands": "7.0.0-beta8",
|
||||||
|
"EntityFramework.SqlServer": "7.0.0-beta8",
|
||||||
|
"Microsoft.AspNet.Authentication.Cookies": "1.0.0-beta8",
|
||||||
|
"Microsoft.AspNet.Authentication.Facebook": "1.0.0-beta8",
|
||||||
|
"Microsoft.AspNet.Authentication.Google": "1.0.0-beta8",
|
||||||
|
"Microsoft.AspNet.Authentication.MicrosoftAccount": "1.0.0-beta8",
|
||||||
|
"Microsoft.AspNet.Authentication.Twitter": "1.0.0-beta8",
|
||||||
|
"Microsoft.AspNet.Diagnostics": "1.0.0-beta8",
|
||||||
|
"Microsoft.AspNet.Diagnostics.Entity": "7.0.0-beta8",
|
||||||
|
"Microsoft.AspNet.Identity.EntityFramework": "3.0.0-beta8",
|
||||||
|
"Microsoft.AspNet.IISPlatformHandler": "1.0.0-beta8",
|
||||||
|
"Microsoft.AspNet.Mvc": "6.0.0-beta8",
|
||||||
|
"Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-beta8",
|
||||||
|
"Microsoft.AspNet.Server.Kestrel": "1.0.0-beta8",
|
||||||
|
"Microsoft.AspNet.StaticFiles": "1.0.0-beta8",
|
||||||
|
"Microsoft.AspNet.Tooling.Razor": "1.0.0-beta8",
|
||||||
|
"Microsoft.Framework.Configuration.Abstractions": "1.0.0-beta8",
|
||||||
|
"Microsoft.Framework.Configuration.Json": "1.0.0-beta8",
|
||||||
|
"Microsoft.Framework.Configuration.UserSecrets": "1.0.0-beta8",
|
||||||
|
"Microsoft.Framework.Logging": "1.0.0-beta8",
|
||||||
|
"Microsoft.Framework.Logging.Console": "1.0.0-beta8",
|
||||||
|
"Microsoft.Framework.Logging.Debug": "1.0.0-beta8",
|
||||||
|
"Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-beta8",
|
||||||
|
"TurboLinks.Net": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"commands": {
|
||||||
|
"web": "Microsoft.AspNet.Server.Kestrel",
|
||||||
|
"ef": "EntityFramework.Commands"
|
||||||
|
},
|
||||||
|
|
||||||
|
"frameworks": {
|
||||||
|
"dnx451": { },
|
||||||
|
"dnxcore50": { }
|
||||||
|
},
|
||||||
|
|
||||||
|
"exclude": [
|
||||||
|
"wwwroot",
|
||||||
|
"node_modules"
|
||||||
|
],
|
||||||
|
"publishExclude": [
|
||||||
|
"**.user",
|
||||||
|
"**.vspscc"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"prepublish": [ "npm install", "bower install", "gulp clean", "gulp min" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/TurboLinks.Net/TurboLinks.Net.xproj
Normal file
20
src/TurboLinks.Net/TurboLinks.Net.xproj
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>b57eee3b-37ff-49b5-8841-84b759df1471</ProjectGuid>
|
||||||
|
<RootNamespace>TurboLinks.Net</RootNamespace>
|
||||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
||||||
53
src/TurboLinks.Net/TurboLinks.cs
Normal file
53
src/TurboLinks.Net/TurboLinks.cs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
|
||||||
|
namespace TurboLinks.Net
|
||||||
|
{
|
||||||
|
public class TurboLinks
|
||||||
|
{
|
||||||
|
private RequestDelegate _next;
|
||||||
|
|
||||||
|
public TurboLinks(RequestDelegate next)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Invoke(HttpContext context)
|
||||||
|
{
|
||||||
|
var memoryStream = new MemoryStream();
|
||||||
|
var bodyStream = context.Response.Body;
|
||||||
|
context.Response.Body = memoryStream;
|
||||||
|
await _next?.Invoke(context);
|
||||||
|
var request = context.Request;
|
||||||
|
var response = context.Response;
|
||||||
|
|
||||||
|
if(!string.IsNullOrWhiteSpace(request.Headers["X-XHR-Referer"]))
|
||||||
|
{
|
||||||
|
context.Response.Cookies.Append("request_method", request.Method, new CookieOptions { HttpOnly = false });
|
||||||
|
if(context.Response.StatusCode == 301 || context.Response.StatusCode == 302)
|
||||||
|
{
|
||||||
|
var uri = new Uri(response.Headers["Location"]);
|
||||||
|
if(uri.Host.Equals(request.Host.Value))
|
||||||
|
{
|
||||||
|
response.Headers["X-XHR-Redirected-To"] = response.Headers["Location"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memoryStream.WriteTo(bodyStream);
|
||||||
|
await bodyStream.FlushAsync();
|
||||||
|
memoryStream.Dispose();
|
||||||
|
bodyStream.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BuilderExtension
|
||||||
|
{
|
||||||
|
public static void UseTurboLinks(this IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
app.UseMiddleware<TurboLinks>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/TurboLinks.Net/project.json
Normal file
27
src/TurboLinks.Net/project.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0-*",
|
||||||
|
"description": "TurboLinks.Net Class Library",
|
||||||
|
"authors": [ "Tommy Parnell" ],
|
||||||
|
"tags": [ "" ],
|
||||||
|
"projectUrl": "",
|
||||||
|
"licenseUrl": "",
|
||||||
|
"dependencies": {
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"dnx451": {
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.AspNet.Http.Abstractions": "1.0.0-beta8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dnxcore50": {
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.CSharp": "4.0.1-beta-23225",
|
||||||
|
"System.Collections": "4.0.11-beta-23225",
|
||||||
|
"System.Linq": "4.0.1-beta-23225",
|
||||||
|
"System.Runtime": "4.0.21-beta-23225",
|
||||||
|
"System.Threading": "4.0.11-beta-23225",
|
||||||
|
"Microsoft.AspNet.Http.Abstractions": "1.0.0-beta8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user