├── .gitignore ├── AspNetCoreRouteDebugger.sln ├── LICENSE ├── README.md ├── RouteDebuggerPage ├── Routes.cshtml └── Routes.cshtml.cs └── SampleProject ├── Controllers ├── AccountController.cs └── RoutesController.cs ├── Data ├── ApplicationDbContext.cs ├── ApplicationUser.cs └── Migrations │ ├── 00000000000000_CreateIdentitySchema.Designer.cs │ ├── 00000000000000_CreateIdentitySchema.cs │ └── ApplicationDbContextModelSnapshot.cs ├── Extensions ├── EmailSenderExtensions.cs └── UrlHelperExtensions.cs ├── Pages ├── Account │ ├── AccessDenied.cshtml │ ├── AccessDenied.cshtml.cs │ ├── ConfirmEmail.cshtml │ ├── ConfirmEmail.cshtml.cs │ ├── ExternalLogin.cshtml │ ├── ExternalLogin.cshtml.cs │ ├── ForgotPassword.cshtml │ ├── ForgotPassword.cshtml.cs │ ├── ForgotPasswordConfirmation.cshtml │ ├── ForgotPasswordConfirmation.cshtml.cs │ ├── Lockout.cshtml │ ├── Lockout.cshtml.cs │ ├── Login.cshtml │ ├── Login.cshtml.cs │ ├── LoginWith2fa.cshtml │ ├── LoginWith2fa.cshtml.cs │ ├── LoginWithRecoveryCode.cshtml │ ├── LoginWithRecoveryCode.cshtml.cs │ ├── Manage │ │ ├── ChangePassword.cshtml │ │ ├── ChangePassword.cshtml.cs │ │ ├── Disable2fa.cshtml │ │ ├── Disable2fa.cshtml.cs │ │ ├── EnableAuthenticator.cshtml │ │ ├── EnableAuthenticator.cshtml.cs │ │ ├── ExternalLogins.cshtml │ │ ├── ExternalLogins.cshtml.cs │ │ ├── GenerateRecoveryCodes.cshtml │ │ ├── GenerateRecoveryCodes.cshtml.cs │ │ ├── Index.cshtml │ │ ├── Index.cshtml.cs │ │ ├── ManageNavPages.cs │ │ ├── ResetAuthenticator.cshtml │ │ ├── ResetAuthenticator.cshtml.cs │ │ ├── SetPassword.cshtml │ │ ├── SetPassword.cshtml.cs │ │ ├── TwoFactorAuthentication.cshtml │ │ ├── TwoFactorAuthentication.cshtml.cs │ │ ├── _Layout.cshtml │ │ ├── _ManageNav.cshtml │ │ ├── _StatusMessage.cshtml │ │ └── _ViewImports.cshtml │ ├── Register.cshtml │ ├── Register.cshtml.cs │ ├── ResetPassword.cshtml │ ├── ResetPassword.cshtml.cs │ ├── ResetPasswordConfirmation.cshtml │ ├── ResetPasswordConfirmation.cshtml.cs │ ├── SignedOut.cshtml │ ├── SignedOut.cshtml.cs │ └── _ViewImports.cshtml ├── Contact.cshtml ├── Contact.cshtml.cs ├── Error.cshtml ├── Error.cshtml.cs ├── Index.cshtml ├── Index.cshtml.cs ├── Routes.cshtml ├── Routes.cshtml.cs ├── _Layout.cshtml ├── _LoginPartial.cshtml ├── _ValidationScriptsPartial.cshtml ├── _ViewImports.cshtml └── _ViewStart.cshtml ├── Program.cs ├── RouteDebugging.csproj ├── Services ├── EmailSender.cs └── IEmailSender.cs ├── appsettings.Development.json ├── appsettings.json └── wwwroot ├── css ├── site.css └── site.min.css ├── favicon.ico ├── images ├── banner1.svg ├── banner2.svg ├── banner3.svg └── banner4.svg ├── js ├── site.js └── site.min.js └── lib ├── bootstrap ├── .bower.json ├── LICENSE └── dist │ ├── css │ ├── bootstrap-theme.css │ ├── bootstrap-theme.css.map │ ├── bootstrap-theme.min.css.map │ ├── bootstrap.css │ ├── bootstrap.css.map │ └── bootstrap.min.css.map │ ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 │ └── js │ ├── bootstrap.js │ └── npm.js ├── jquery-validation-unobtrusive ├── .bower.json ├── jquery.validate.unobtrusive.js └── jquery.validate.unobtrusive.min.js ├── jquery-validation ├── .bower.json ├── LICENSE.md └── dist │ ├── additional-methods.js │ └── jquery.validate.js └── jquery ├── .bower.json ├── LICENSE.txt └── dist ├── jquery.js └── jquery.min.map /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | -------------------------------------------------------------------------------- /AspNetCoreRouteDebugger.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RouteDebugging", "SampleProject\RouteDebugging.csproj", "{9AAD7F70-3F48-C9CB-876F-C419671828BC}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {9AAD7F70-3F48-C9CB-876F-C419671828BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {9AAD7F70-3F48-C9CB-876F-C419671828BC}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {9AAD7F70-3F48-C9CB-876F-C419671828BC}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {9AAD7F70-3F48-C9CB-876F-C419671828BC}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Steve Smith 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AspNetCoreRouteDebugger 2 | 3 | An ASP.NET Core 9 Route Debugger implemented as a Razor Page. Read more about [Debugging ASP.NET Core Routes](https://ardalis.com/debugging-aspnet-core-routes). 4 | 5 | ## Sample 6 | 7 | Clone the rep. Run the sample project. Navigate to /routes. You should see a list of routes. 8 | 9 | ## Adding to your Project 10 | 11 | Grab the two files from the RouteDebuggerPage folder. Drop them into a Pages folder in your ASP.NET Core app (2.0 or greater). Rename if desired (and lock down from public access). Fix namespaces if desired. That should be all you need to do! 12 | 13 | ### I'm Not Using Razor Pages! 14 | 15 | Grab the [RoutesController.cs](/SampleProject/Controllers/RoutesController.cs) file, which currently returns JSON. Write a simple page that consumes the JSON (via an API call), or modify the Routes.cshtml file to be a view and have the controller return a view instead. Pull requests accepted if someone wants to make this a bit cleaner. 16 | 17 | #### Original Idea 18 | 19 | Credit for the idea behind this sample goes to [this issue](https://github.com/aspnet/Mvc/issues/6330). 20 | -------------------------------------------------------------------------------- /RouteDebuggerPage/Routes.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model RouteDebugging.Pages.RoutesModel 3 | @{ 4 | ViewData["Title"] = "Routes"; 5 | } 6 | 7 |

@ViewData["Title"]

8 |

Route Debug Info by @@ardalis

9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | @foreach (var route in Model.Routes) 23 | { 24 | @if (!String.IsNullOrEmpty(route.Template)) 25 | { 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | } 35 | } 36 | 37 |
Route TemplateControllerActionConstraints/VerbsRouteValuesName
@route.Template@route.Controller@route.Action@route.Constraint@route.RouteValues@route.Name
38 | -------------------------------------------------------------------------------- /RouteDebuggerPage/Routes.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.RazorPages; 2 | using Microsoft.AspNetCore.Mvc.Infrastructure; 3 | using Microsoft.AspNetCore.Mvc.Controllers; 4 | using System.Text.Json; 5 | 6 | namespace RouteDebugging.Pages; 7 | 8 | public class RoutesModel : PageModel 9 | { 10 | private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider; 11 | 12 | public RoutesModel(IActionDescriptorCollectionProvider actionDescriptorCollectionProvider) 13 | { 14 | this._actionDescriptorCollectionProvider = actionDescriptorCollectionProvider; 15 | } 16 | 17 | public List Routes { get; set; } = new(); 18 | 19 | public void OnGet() 20 | { 21 | // DisplayName 22 | var PageRoutes = _actionDescriptorCollectionProvider.ActionDescriptors.Items.OfType() 23 | .Select(x => new RouteInfo 24 | { 25 | Action = "", 26 | Controller = x.DisplayName ?? string.Empty, 27 | Name = x.AttributeRouteInfo?.Name ?? string.Empty, 28 | Template = x.AttributeRouteInfo?.Template ?? string.Empty, 29 | Constraint = x.ActionConstraints == null ? "" : JsonSerializer.Serialize(x.ActionConstraints), 30 | RouteValues = string.Join(',', x.RouteValues) 31 | }) 32 | .OrderBy(r => r.Template) 33 | .ToList(); 34 | 35 | var ViewRoutes = _actionDescriptorCollectionProvider.ActionDescriptors.Items.OfType() 36 | .Select(x => new RouteInfo 37 | { 38 | Action = x.RouteValues["Action"] ?? string.Empty, 39 | Controller = x.RouteValues["Controller"] ?? string.Empty, 40 | Name = x.AttributeRouteInfo?.Name ?? string.Empty, 41 | Template = x.AttributeRouteInfo?.Template ?? string.Empty, 42 | Constraint = x.ActionConstraints == null ? "" : JsonSerializer.Serialize(x.ActionConstraints), 43 | }) 44 | .OrderBy(r => r.Template) 45 | .ToList(); 46 | 47 | Routes = PageRoutes.Concat(ViewRoutes).ToList(); 48 | } 49 | 50 | public class RouteInfo 51 | { 52 | public string Template { get; set; } = string.Empty; 53 | public string Name { get; set; } = string.Empty; 54 | public string Controller { get; set; } = string.Empty; 55 | public string Action { get; set; } = string.Empty; 56 | public string Constraint { get; set; } = string.Empty; 57 | public string RouteValues { get; set; } = string.Empty; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /SampleProject/Controllers/AccountController.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Identity; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.Logging; 5 | using RouteDebugging.Data; 6 | 7 | namespace RouteDebugging.Controllers 8 | { 9 | [Route("[controller]/[action]")] 10 | public class AccountController : Controller 11 | { 12 | private readonly SignInManager _signInManager; 13 | private readonly ILogger _logger; 14 | 15 | public AccountController(SignInManager signInManager, ILogger logger) 16 | { 17 | _signInManager = signInManager; 18 | _logger = logger; 19 | } 20 | 21 | [HttpPost] 22 | [ValidateAntiForgeryToken] 23 | public async Task Logout() 24 | { 25 | await _signInManager.SignOutAsync(); 26 | _logger.LogInformation("User logged out."); 27 | return RedirectToPage("/Index"); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SampleProject/Controllers/RoutesController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.ActionConstraints; 3 | using Microsoft.AspNetCore.Mvc.Controllers; 4 | using Microsoft.AspNetCore.Mvc.Infrastructure; 5 | using Microsoft.AspNetCore.Mvc.RazorPages; 6 | using System.Text.Json; 7 | using static RouteDebugging.Pages.RoutesModel; 8 | 9 | namespace RouteDebugging.Controllers 10 | { 11 | [Route("[controller]/[action]")] 12 | public class RoutesController : Controller 13 | { 14 | private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider; 15 | 16 | public RoutesController(IActionDescriptorCollectionProvider actionDescriptorCollectionProvider) 17 | { 18 | _actionDescriptorCollectionProvider = actionDescriptorCollectionProvider; 19 | } 20 | 21 | [HttpGet("/RoutesController")] 22 | public IActionResult Index() 23 | { 24 | var pageRoutes = _actionDescriptorCollectionProvider.ActionDescriptors.Items.OfType() 25 | .Select(x => new RouteInfo 26 | { 27 | Action = "", 28 | Controller = x.DisplayName, 29 | Name = x.AttributeRouteInfo?.Name, 30 | Template = x.AttributeRouteInfo?.Template, 31 | Constraint = x.ActionConstraints == null ? "" : JsonSerializer.Serialize(x.ActionConstraints), 32 | RouteValues = string.Join(',', x.RouteValues) 33 | }) 34 | .OrderBy(r => r.Template); 35 | 36 | var viewRoutes = _actionDescriptorCollectionProvider.ActionDescriptors.Items.OfType() 37 | .Select(x => new RouteInfo 38 | { 39 | Action = x.RouteValues["Action"], 40 | Controller = x.RouteValues["Controller"], 41 | Name = x.AttributeRouteInfo?.Name, 42 | Template = x.AttributeRouteInfo?.Template, 43 | Constraint = x.ActionConstraints == null ? "" : JsonSerializer.Serialize(x.ActionConstraints), 44 | }) 45 | .OrderBy(r => r.Template); 46 | 47 | var routes = pageRoutes.Concat(viewRoutes).ToList(); 48 | 49 | return Json(routes, new JsonSerializerOptions 50 | { 51 | WriteIndented = true, 52 | }); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /SampleProject/Data/ApplicationDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace RouteDebugging.Data 5 | { 6 | public class ApplicationDbContext : IdentityDbContext 7 | { 8 | public ApplicationDbContext(DbContextOptions options) 9 | : base(options) 10 | { 11 | } 12 | 13 | protected override void OnModelCreating(ModelBuilder builder) 14 | { 15 | base.OnModelCreating(builder); 16 | // Customize the ASP.NET Identity model and override the defaults if needed. 17 | // For example, you can rename the ASP.NET Identity table names and more. 18 | // Add your customizations after calling base.OnModelCreating(builder); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SampleProject/Data/ApplicationUser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Identity; 6 | 7 | namespace RouteDebugging.Data 8 | { 9 | // Add profile data for application users by adding properties to the ApplicationUser class 10 | public class ApplicationUser : IdentityUser 11 | { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /SampleProject/Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.EntityFrameworkCore; 6 | using Microsoft.EntityFrameworkCore.Infrastructure; 7 | using Microsoft.EntityFrameworkCore.Metadata; 8 | using Microsoft.EntityFrameworkCore.Migrations; 9 | 10 | namespace RouteDebugging.Data.Migrations 11 | { 12 | [DbContext(typeof(ApplicationDbContext))] 13 | [Migration("00000000000000_CreateIdentitySchema")] 14 | partial class CreateIdentitySchema 15 | { 16 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 17 | { 18 | modelBuilder 19 | .HasAnnotation("ProductVersion", "1.0.0-rc3") 20 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 21 | 22 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => 23 | { 24 | b.Property("Id"); 25 | 26 | b.Property("ConcurrencyStamp") 27 | .IsConcurrencyToken(); 28 | 29 | b.Property("Name") 30 | .HasAnnotation("MaxLength", 256); 31 | 32 | b.Property("NormalizedName") 33 | .HasAnnotation("MaxLength", 256); 34 | 35 | b.HasKey("Id"); 36 | 37 | b.HasIndex("NormalizedName") 38 | .HasName("RoleNameIndex"); 39 | 40 | b.ToTable("AspNetRoles"); 41 | }); 42 | 43 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => 44 | { 45 | b.Property("Id") 46 | .ValueGeneratedOnAdd(); 47 | 48 | b.Property("ClaimType"); 49 | 50 | b.Property("ClaimValue"); 51 | 52 | b.Property("RoleId") 53 | .IsRequired(); 54 | 55 | b.HasKey("Id"); 56 | 57 | b.HasIndex("RoleId"); 58 | 59 | b.ToTable("AspNetRoleClaims"); 60 | }); 61 | 62 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => 63 | { 64 | b.Property("Id") 65 | .ValueGeneratedOnAdd(); 66 | 67 | b.Property("ClaimType"); 68 | 69 | b.Property("ClaimValue"); 70 | 71 | b.Property("UserId") 72 | .IsRequired(); 73 | 74 | b.HasKey("Id"); 75 | 76 | b.HasIndex("UserId"); 77 | 78 | b.ToTable("AspNetUserClaims"); 79 | }); 80 | 81 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => 82 | { 83 | b.Property("LoginProvider"); 84 | 85 | b.Property("ProviderKey"); 86 | 87 | b.Property("ProviderDisplayName"); 88 | 89 | b.Property("UserId") 90 | .IsRequired(); 91 | 92 | b.HasKey("LoginProvider", "ProviderKey"); 93 | 94 | b.HasIndex("UserId"); 95 | 96 | b.ToTable("AspNetUserLogins"); 97 | }); 98 | 99 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => 100 | { 101 | b.Property("UserId"); 102 | 103 | b.Property("RoleId"); 104 | 105 | b.HasKey("UserId", "RoleId"); 106 | 107 | b.HasIndex("RoleId"); 108 | 109 | b.HasIndex("UserId"); 110 | 111 | b.ToTable("AspNetUserRoles"); 112 | }); 113 | 114 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => 115 | { 116 | b.Property("UserId"); 117 | 118 | b.Property("LoginProvider"); 119 | 120 | b.Property("Name"); 121 | 122 | b.Property("Value"); 123 | 124 | b.HasKey("UserId", "LoginProvider", "Name"); 125 | 126 | b.ToTable("AspNetUserTokens"); 127 | }); 128 | 129 | modelBuilder.Entity("RouteDebugging.Models.ApplicationUser", b => 130 | { 131 | b.Property("Id"); 132 | 133 | b.Property("AccessFailedCount"); 134 | 135 | b.Property("ConcurrencyStamp") 136 | .IsConcurrencyToken(); 137 | 138 | b.Property("Email") 139 | .HasAnnotation("MaxLength", 256); 140 | 141 | b.Property("EmailConfirmed"); 142 | 143 | b.Property("LockoutEnabled"); 144 | 145 | b.Property("LockoutEnd"); 146 | 147 | b.Property("NormalizedEmail") 148 | .HasAnnotation("MaxLength", 256); 149 | 150 | b.Property("NormalizedUserName") 151 | .HasAnnotation("MaxLength", 256); 152 | 153 | b.Property("PasswordHash"); 154 | 155 | b.Property("PhoneNumber"); 156 | 157 | b.Property("PhoneNumberConfirmed"); 158 | 159 | b.Property("SecurityStamp"); 160 | 161 | b.Property("TwoFactorEnabled"); 162 | 163 | b.Property("UserName") 164 | .HasAnnotation("MaxLength", 256); 165 | 166 | b.HasKey("Id"); 167 | 168 | b.HasIndex("NormalizedEmail") 169 | .HasName("EmailIndex"); 170 | 171 | b.HasIndex("NormalizedUserName") 172 | .IsUnique() 173 | .HasName("UserNameIndex"); 174 | 175 | b.ToTable("AspNetUsers"); 176 | }); 177 | 178 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => 179 | { 180 | b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") 181 | .WithMany("Claims") 182 | .HasForeignKey("RoleId") 183 | .OnDelete(DeleteBehavior.Cascade); 184 | }); 185 | 186 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => 187 | { 188 | b.HasOne("RouteDebugging.Models.ApplicationUser") 189 | .WithMany("Claims") 190 | .HasForeignKey("UserId") 191 | .OnDelete(DeleteBehavior.Cascade); 192 | }); 193 | 194 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => 195 | { 196 | b.HasOne("RouteDebugging.Models.ApplicationUser") 197 | .WithMany("Logins") 198 | .HasForeignKey("UserId") 199 | .OnDelete(DeleteBehavior.Cascade); 200 | }); 201 | 202 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => 203 | { 204 | b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") 205 | .WithMany("Users") 206 | .HasForeignKey("RoleId") 207 | .OnDelete(DeleteBehavior.Cascade); 208 | 209 | b.HasOne("RouteDebugging.Models.ApplicationUser") 210 | .WithMany("Roles") 211 | .HasForeignKey("UserId") 212 | .OnDelete(DeleteBehavior.Cascade); 213 | }); 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /SampleProject/Data/Migrations/00000000000000_CreateIdentitySchema.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.EntityFrameworkCore.Metadata; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | 8 | namespace RouteDebugging.Data.Migrations 9 | { 10 | public partial class CreateIdentitySchema : Migration 11 | { 12 | protected override void Up(MigrationBuilder migrationBuilder) 13 | { 14 | migrationBuilder.CreateTable( 15 | name: "AspNetRoles", 16 | columns: table => new 17 | { 18 | Id = table.Column(nullable: false), 19 | ConcurrencyStamp = table.Column(nullable: true), 20 | Name = table.Column(maxLength: 256, nullable: true), 21 | NormalizedName = table.Column(maxLength: 256, nullable: true) 22 | }, 23 | constraints: table => 24 | { 25 | table.PrimaryKey("PK_AspNetRoles", x => x.Id); 26 | }); 27 | 28 | migrationBuilder.CreateTable( 29 | name: "AspNetUserTokens", 30 | columns: table => new 31 | { 32 | UserId = table.Column(nullable: false), 33 | LoginProvider = table.Column(nullable: false), 34 | Name = table.Column(nullable: false), 35 | Value = table.Column(nullable: true) 36 | }, 37 | constraints: table => 38 | { 39 | table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); 40 | }); 41 | 42 | migrationBuilder.CreateTable( 43 | name: "AspNetUsers", 44 | columns: table => new 45 | { 46 | Id = table.Column(nullable: false), 47 | AccessFailedCount = table.Column(nullable: false), 48 | ConcurrencyStamp = table.Column(nullable: true), 49 | Email = table.Column(maxLength: 256, nullable: true), 50 | EmailConfirmed = table.Column(nullable: false), 51 | LockoutEnabled = table.Column(nullable: false), 52 | LockoutEnd = table.Column(nullable: true), 53 | NormalizedEmail = table.Column(maxLength: 256, nullable: true), 54 | NormalizedUserName = table.Column(maxLength: 256, nullable: true), 55 | PasswordHash = table.Column(nullable: true), 56 | PhoneNumber = table.Column(nullable: true), 57 | PhoneNumberConfirmed = table.Column(nullable: false), 58 | SecurityStamp = table.Column(nullable: true), 59 | TwoFactorEnabled = table.Column(nullable: false), 60 | UserName = table.Column(maxLength: 256, nullable: true) 61 | }, 62 | constraints: table => 63 | { 64 | table.PrimaryKey("PK_AspNetUsers", x => x.Id); 65 | }); 66 | 67 | migrationBuilder.CreateTable( 68 | name: "AspNetRoleClaims", 69 | columns: table => new 70 | { 71 | Id = table.Column(nullable: false) 72 | .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), 73 | ClaimType = table.Column(nullable: true), 74 | ClaimValue = table.Column(nullable: true), 75 | RoleId = table.Column(nullable: false) 76 | }, 77 | constraints: table => 78 | { 79 | table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); 80 | table.ForeignKey( 81 | name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", 82 | column: x => x.RoleId, 83 | principalTable: "AspNetRoles", 84 | principalColumn: "Id", 85 | onDelete: ReferentialAction.Cascade); 86 | }); 87 | 88 | migrationBuilder.CreateTable( 89 | name: "AspNetUserClaims", 90 | columns: table => new 91 | { 92 | Id = table.Column(nullable: false) 93 | .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), 94 | ClaimType = table.Column(nullable: true), 95 | ClaimValue = table.Column(nullable: true), 96 | UserId = table.Column(nullable: false) 97 | }, 98 | constraints: table => 99 | { 100 | table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); 101 | table.ForeignKey( 102 | name: "FK_AspNetUserClaims_AspNetUsers_UserId", 103 | column: x => x.UserId, 104 | principalTable: "AspNetUsers", 105 | principalColumn: "Id", 106 | onDelete: ReferentialAction.Cascade); 107 | }); 108 | 109 | migrationBuilder.CreateTable( 110 | name: "AspNetUserLogins", 111 | columns: table => new 112 | { 113 | LoginProvider = table.Column(nullable: false), 114 | ProviderKey = table.Column(nullable: false), 115 | ProviderDisplayName = table.Column(nullable: true), 116 | UserId = table.Column(nullable: false) 117 | }, 118 | constraints: table => 119 | { 120 | table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); 121 | table.ForeignKey( 122 | name: "FK_AspNetUserLogins_AspNetUsers_UserId", 123 | column: x => x.UserId, 124 | principalTable: "AspNetUsers", 125 | principalColumn: "Id", 126 | onDelete: ReferentialAction.Cascade); 127 | }); 128 | 129 | migrationBuilder.CreateTable( 130 | name: "AspNetUserRoles", 131 | columns: table => new 132 | { 133 | UserId = table.Column(nullable: false), 134 | RoleId = table.Column(nullable: false) 135 | }, 136 | constraints: table => 137 | { 138 | table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); 139 | table.ForeignKey( 140 | name: "FK_AspNetUserRoles_AspNetRoles_RoleId", 141 | column: x => x.RoleId, 142 | principalTable: "AspNetRoles", 143 | principalColumn: "Id", 144 | onDelete: ReferentialAction.Cascade); 145 | table.ForeignKey( 146 | name: "FK_AspNetUserRoles_AspNetUsers_UserId", 147 | column: x => x.UserId, 148 | principalTable: "AspNetUsers", 149 | principalColumn: "Id", 150 | onDelete: ReferentialAction.Cascade); 151 | }); 152 | 153 | migrationBuilder.CreateIndex( 154 | name: "RoleNameIndex", 155 | table: "AspNetRoles", 156 | column: "NormalizedName"); 157 | 158 | migrationBuilder.CreateIndex( 159 | name: "IX_AspNetRoleClaims_RoleId", 160 | table: "AspNetRoleClaims", 161 | column: "RoleId"); 162 | 163 | migrationBuilder.CreateIndex( 164 | name: "IX_AspNetUserClaims_UserId", 165 | table: "AspNetUserClaims", 166 | column: "UserId"); 167 | 168 | migrationBuilder.CreateIndex( 169 | name: "IX_AspNetUserLogins_UserId", 170 | table: "AspNetUserLogins", 171 | column: "UserId"); 172 | 173 | migrationBuilder.CreateIndex( 174 | name: "IX_AspNetUserRoles_RoleId", 175 | table: "AspNetUserRoles", 176 | column: "RoleId"); 177 | 178 | migrationBuilder.CreateIndex( 179 | name: "IX_AspNetUserRoles_UserId", 180 | table: "AspNetUserRoles", 181 | column: "UserId"); 182 | 183 | migrationBuilder.CreateIndex( 184 | name: "EmailIndex", 185 | table: "AspNetUsers", 186 | column: "NormalizedEmail"); 187 | 188 | migrationBuilder.CreateIndex( 189 | name: "UserNameIndex", 190 | table: "AspNetUsers", 191 | column: "NormalizedUserName", 192 | unique: true); 193 | } 194 | 195 | protected override void Down(MigrationBuilder migrationBuilder) 196 | { 197 | migrationBuilder.DropTable( 198 | name: "AspNetRoleClaims"); 199 | 200 | migrationBuilder.DropTable( 201 | name: "AspNetUserClaims"); 202 | 203 | migrationBuilder.DropTable( 204 | name: "AspNetUserLogins"); 205 | 206 | migrationBuilder.DropTable( 207 | name: "AspNetUserRoles"); 208 | 209 | migrationBuilder.DropTable( 210 | name: "AspNetUserTokens"); 211 | 212 | migrationBuilder.DropTable( 213 | name: "AspNetRoles"); 214 | 215 | migrationBuilder.DropTable( 216 | name: "AspNetUsers"); 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /SampleProject/Data/Migrations/ApplicationDbContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.EntityFrameworkCore; 6 | using Microsoft.EntityFrameworkCore.Infrastructure; 7 | using Microsoft.EntityFrameworkCore.Metadata; 8 | using Microsoft.EntityFrameworkCore.Migrations; 9 | 10 | namespace RouteDebugging.Data.Migrations 11 | { 12 | [DbContext(typeof(ApplicationDbContext))] 13 | partial class ApplicationDbContextModelSnapshot : ModelSnapshot 14 | { 15 | protected override void BuildModel(ModelBuilder modelBuilder) 16 | { 17 | modelBuilder 18 | .HasAnnotation("ProductVersion", "1.0.0-rc3") 19 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 20 | 21 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole", b => 22 | { 23 | b.Property("Id"); 24 | 25 | b.Property("ConcurrencyStamp") 26 | .IsConcurrencyToken(); 27 | 28 | b.Property("Name") 29 | .HasAnnotation("MaxLength", 256); 30 | 31 | b.Property("NormalizedName") 32 | .HasAnnotation("MaxLength", 256); 33 | 34 | b.HasKey("Id"); 35 | 36 | b.HasIndex("NormalizedName") 37 | .HasName("RoleNameIndex"); 38 | 39 | b.ToTable("AspNetRoles"); 40 | }); 41 | 42 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRoleClaim", b => 43 | { 44 | b.Property("Id") 45 | .ValueGeneratedOnAdd(); 46 | 47 | b.Property("ClaimType"); 48 | 49 | b.Property("ClaimValue"); 50 | 51 | b.Property("RoleId") 52 | .IsRequired(); 53 | 54 | b.HasKey("Id"); 55 | 56 | b.HasIndex("RoleId"); 57 | 58 | b.ToTable("AspNetRoleClaims"); 59 | }); 60 | 61 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserClaim", b => 62 | { 63 | b.Property("Id") 64 | .ValueGeneratedOnAdd(); 65 | 66 | b.Property("ClaimType"); 67 | 68 | b.Property("ClaimValue"); 69 | 70 | b.Property("UserId") 71 | .IsRequired(); 72 | 73 | b.HasKey("Id"); 74 | 75 | b.HasIndex("UserId"); 76 | 77 | b.ToTable("AspNetUserClaims"); 78 | }); 79 | 80 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin", b => 81 | { 82 | b.Property("LoginProvider"); 83 | 84 | b.Property("ProviderKey"); 85 | 86 | b.Property("ProviderDisplayName"); 87 | 88 | b.Property("UserId") 89 | .IsRequired(); 90 | 91 | b.HasKey("LoginProvider", "ProviderKey"); 92 | 93 | b.HasIndex("UserId"); 94 | 95 | b.ToTable("AspNetUserLogins"); 96 | }); 97 | 98 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserRole", b => 99 | { 100 | b.Property("UserId"); 101 | 102 | b.Property("RoleId"); 103 | 104 | b.HasKey("UserId", "RoleId"); 105 | 106 | b.HasIndex("RoleId"); 107 | 108 | b.HasIndex("UserId"); 109 | 110 | b.ToTable("AspNetUserRoles"); 111 | }); 112 | 113 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserToken", b => 114 | { 115 | b.Property("UserId"); 116 | 117 | b.Property("LoginProvider"); 118 | 119 | b.Property("Name"); 120 | 121 | b.Property("Value"); 122 | 123 | b.HasKey("UserId", "LoginProvider", "Name"); 124 | 125 | b.ToTable("AspNetUserTokens"); 126 | }); 127 | 128 | modelBuilder.Entity("RouteDebugging.Models.ApplicationUser", b => 129 | { 130 | b.Property("Id"); 131 | 132 | b.Property("AccessFailedCount"); 133 | 134 | b.Property("ConcurrencyStamp") 135 | .IsConcurrencyToken(); 136 | 137 | b.Property("Email") 138 | .HasAnnotation("MaxLength", 256); 139 | 140 | b.Property("EmailConfirmed"); 141 | 142 | b.Property("LockoutEnabled"); 143 | 144 | b.Property("LockoutEnd"); 145 | 146 | b.Property("NormalizedEmail") 147 | .HasAnnotation("MaxLength", 256); 148 | 149 | b.Property("NormalizedUserName") 150 | .HasAnnotation("MaxLength", 256); 151 | 152 | b.Property("PasswordHash"); 153 | 154 | b.Property("PhoneNumber"); 155 | 156 | b.Property("PhoneNumberConfirmed"); 157 | 158 | b.Property("SecurityStamp"); 159 | 160 | b.Property("TwoFactorEnabled"); 161 | 162 | b.Property("UserName") 163 | .HasAnnotation("MaxLength", 256); 164 | 165 | b.HasKey("Id"); 166 | 167 | b.HasIndex("NormalizedEmail") 168 | .HasName("EmailIndex"); 169 | 170 | b.HasIndex("NormalizedUserName") 171 | .IsUnique() 172 | .HasName("UserNameIndex"); 173 | 174 | b.ToTable("AspNetUsers"); 175 | }); 176 | 177 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRoleClaim", b => 178 | { 179 | b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole") 180 | .WithMany("Claims") 181 | .HasForeignKey("RoleId") 182 | .OnDelete(DeleteBehavior.Cascade); 183 | }); 184 | 185 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserClaim", b => 186 | { 187 | b.HasOne("RouteDebugging.Models.ApplicationUser") 188 | .WithMany("Claims") 189 | .HasForeignKey("UserId") 190 | .OnDelete(DeleteBehavior.Cascade); 191 | }); 192 | 193 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin", b => 194 | { 195 | b.HasOne("RouteDebugging.Models.ApplicationUser") 196 | .WithMany("Logins") 197 | .HasForeignKey("UserId") 198 | .OnDelete(DeleteBehavior.Cascade); 199 | }); 200 | 201 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserRole", b => 202 | { 203 | b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole") 204 | .WithMany("Users") 205 | .HasForeignKey("RoleId") 206 | .OnDelete(DeleteBehavior.Cascade); 207 | 208 | b.HasOne("RouteDebugging.Models.ApplicationUser") 209 | .WithMany("Roles") 210 | .HasForeignKey("UserId") 211 | .OnDelete(DeleteBehavior.Cascade); 212 | }); 213 | } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /SampleProject/Extensions/EmailSenderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Encodings.Web; 2 | 3 | namespace RouteDebugging.Services 4 | { 5 | public static class EmailSenderExtensions 6 | { 7 | public static Task SendEmailConfirmationAsync(this IEmailSender emailSender, string email, string link) 8 | { 9 | return emailSender.SendEmailAsync(email, "Confirm your email", 10 | $"Please confirm your account by clicking here."); 11 | } 12 | 13 | public static Task SendResetPasswordAsync(this IEmailSender emailSender, string email, string callbackUrl) 14 | { 15 | return emailSender.SendEmailAsync(email, "Reset Password", 16 | $"Please reset your password by clicking here."); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SampleProject/Extensions/UrlHelperExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.AspNetCore.Mvc 2 | { 3 | public static class UrlHelperExtensions 4 | { 5 | public static string GetLocalUrl(this IUrlHelper urlHelper, string localUrl) 6 | { 7 | if (!urlHelper.IsLocalUrl(localUrl)) 8 | { 9 | return urlHelper.Page("/Index"); 10 | } 11 | 12 | return localUrl; 13 | } 14 | 15 | public static string EmailConfirmationLink(this IUrlHelper urlHelper, string userId, string code, string scheme) 16 | { 17 | return urlHelper.Page( 18 | "/Account/ConfirmEmail", 19 | pageHandler: null, 20 | values: new { userId, code }, 21 | protocol: scheme); 22 | } 23 | 24 | public static string ResetPasswordCallbackLink(this IUrlHelper urlHelper, string userId, string code, string scheme) 25 | { 26 | return urlHelper.Page( 27 | "/Account/ResetPassword", 28 | pageHandler: null, 29 | values: new { userId, code }, 30 | protocol: scheme); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/AccessDenied.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model AccessDeniedModel 3 | @{ 4 | ViewData["Title"] = "Access denied"; 5 | } 6 | 7 |
8 |

ViewData["Title"]

9 |

You do not have access to this resource.

10 |
11 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/AccessDenied.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc.RazorPages; 6 | 7 | namespace RouteDebugging.Pages.Account 8 | { 9 | public class AccessDeniedModel : PageModel 10 | { 11 | public void OnGet() 12 | { 13 | 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/ConfirmEmail.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ConfirmEmailModel 3 | @{ 4 | ViewData["Title"] = "Confirm email"; 5 | } 6 | 7 |

@ViewData["Title"]

8 |
9 |

10 | Thank you for confirming your email. 11 |

12 |
13 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/ConfirmEmail.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Identity; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.RazorPages; 8 | using RouteDebugging.Data; 9 | 10 | namespace RouteDebugging.Pages.Account 11 | { 12 | public class ConfirmEmailModel : PageModel 13 | { 14 | private readonly UserManager _userManager; 15 | 16 | public ConfirmEmailModel(UserManager userManager) 17 | { 18 | _userManager = userManager; 19 | } 20 | 21 | public async Task OnGetAsync(string userId, string code) 22 | { 23 | if (userId == null || code == null) 24 | { 25 | return RedirectToPage("/Index"); 26 | } 27 | 28 | var user = await _userManager.FindByIdAsync(userId); 29 | if (user == null) 30 | { 31 | throw new ApplicationException($"Unable to load user with ID '{userId}'."); 32 | } 33 | 34 | var result = await _userManager.ConfirmEmailAsync(user, code); 35 | if (!result.Succeeded) 36 | { 37 | throw new ApplicationException($"Error confirming email for user with ID '{userId}':"); 38 | } 39 | 40 | return Page(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/ExternalLogin.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ExternalLoginModel 3 | @{ 4 | ViewData["Title"] = "Register"; 5 | } 6 | 7 |

@ViewData["Title"]

8 |

Associate your @Model.LoginProvider account.

9 |
10 | 11 |

12 | You've successfully authenticated with @Model.LoginProvider. 13 | Please enter an email address for this site below and click the Register button to finish 14 | logging in. 15 |

16 | 17 |
18 |
19 |
20 |
21 |
22 | 23 | 24 | 25 |
26 | 27 |
28 |
29 |
30 | 31 | @section Scripts { 32 | @await Html.PartialAsync("_ValidationScriptsPartial") 33 | } 34 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/ExternalLogin.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Security.Claims; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore.Identity; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Microsoft.AspNetCore.Mvc.RazorPages; 10 | using Microsoft.Extensions.Logging; 11 | using RouteDebugging.Data; 12 | 13 | namespace RouteDebugging.Pages.Account 14 | { 15 | public class ExternalLoginModel : PageModel 16 | { 17 | private readonly SignInManager _signInManager; 18 | private readonly UserManager _userManager; 19 | private readonly ILogger _logger; 20 | 21 | public ExternalLoginModel( 22 | SignInManager signInManager, 23 | UserManager userManager, 24 | ILogger logger) 25 | { 26 | _signInManager = signInManager; 27 | _userManager = userManager; 28 | _logger = logger; 29 | } 30 | 31 | [BindProperty] 32 | public InputModel Input { get; set; } 33 | 34 | public string LoginProvider { get; set; } 35 | 36 | public string ReturnUrl { get; set; } 37 | 38 | [TempData] 39 | public string ErrorMessage { get; set; } 40 | 41 | public class InputModel 42 | { 43 | [Required] 44 | [EmailAddress] 45 | public string Email { get; set; } 46 | } 47 | 48 | public IActionResult OnGetAsync() 49 | { 50 | return RedirectToPage("./Login"); 51 | } 52 | 53 | public IActionResult OnPost(string provider, string returnUrl = null) 54 | { 55 | // Request a redirect to the external login provider. 56 | var redirectUrl = Url.Page("./ExternalLogin", pageHandler: "Callback", values: new { returnUrl }); 57 | var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); 58 | return new ChallengeResult(provider, properties); 59 | } 60 | 61 | public async Task OnGetCallbackAsync(string returnUrl = null, string remoteError = null) 62 | { 63 | if (remoteError != null) 64 | { 65 | ErrorMessage = $"Error from external provider: {remoteError}"; 66 | return RedirectToPage("./Login"); 67 | } 68 | var info = await _signInManager.GetExternalLoginInfoAsync(); 69 | if (info == null) 70 | { 71 | return RedirectToPage("./Login"); 72 | } 73 | 74 | // Sign in the user with this external login provider if the user already has a login. 75 | var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor : true); 76 | if (result.Succeeded) 77 | { 78 | _logger.LogInformation("{Name} logged in with {LoginProvider} provider.", info.Principal.Identity.Name, info.LoginProvider); 79 | return LocalRedirect(Url.GetLocalUrl(returnUrl)); 80 | } 81 | if (result.IsLockedOut) 82 | { 83 | return RedirectToPage("./Lockout"); 84 | } 85 | else 86 | { 87 | // If the user does not have an account, then ask the user to create an account. 88 | ReturnUrl = returnUrl; 89 | LoginProvider = info.LoginProvider; 90 | if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email)) 91 | { 92 | Input = new InputModel 93 | { 94 | Email = info.Principal.FindFirstValue(ClaimTypes.Email) 95 | }; 96 | } 97 | return Page(); 98 | } 99 | } 100 | 101 | public async Task OnPostConfirmationAsync(string returnUrl = null) 102 | { 103 | if (ModelState.IsValid) 104 | { 105 | // Get the information about the user from the external login provider 106 | var info = await _signInManager.GetExternalLoginInfoAsync(); 107 | if (info == null) 108 | { 109 | throw new ApplicationException("Error loading external login information during confirmation."); 110 | } 111 | var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email }; 112 | var result = await _userManager.CreateAsync(user); 113 | if (result.Succeeded) 114 | { 115 | result = await _userManager.AddLoginAsync(user, info); 116 | if (result.Succeeded) 117 | { 118 | await _signInManager.SignInAsync(user, isPersistent: false); 119 | _logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider); 120 | return LocalRedirect(Url.GetLocalUrl(returnUrl)); 121 | } 122 | } 123 | foreach (var error in result.Errors) 124 | { 125 | ModelState.AddModelError(string.Empty, error.Description); 126 | } 127 | } 128 | 129 | ReturnUrl = returnUrl; 130 | return Page(); 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/ForgotPassword.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ForgotPasswordModel 3 | @{ 4 | ViewData["Title"] = "Forgot your password?"; 5 | } 6 | 7 |

@ViewData["Title"]

8 |

Enter your email.

9 |
10 |
11 |
12 |
13 |
14 |
15 | 16 | 17 | 18 |
19 | 20 |
21 |
22 |
23 | 24 | @section Scripts { 25 | @await Html.PartialAsync("_ValidationScriptsPartial") 26 | } 27 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/ForgotPassword.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Identity; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.RazorPages; 8 | using RouteDebugging.Data; 9 | using RouteDebugging.Services; 10 | 11 | namespace RouteDebugging.Pages.Account 12 | { 13 | public class ForgotPasswordModel : PageModel 14 | { 15 | private readonly UserManager _userManager; 16 | private readonly IEmailSender _emailSender; 17 | 18 | public ForgotPasswordModel(UserManager userManager, IEmailSender emailSender) 19 | { 20 | _userManager = userManager; 21 | _emailSender = emailSender; 22 | } 23 | 24 | [BindProperty] 25 | public InputModel Input { get; set; } 26 | 27 | public class InputModel 28 | { 29 | [Required] 30 | [EmailAddress] 31 | public string Email { get; set; } 32 | } 33 | 34 | public async Task OnPostAsync() 35 | { 36 | if (ModelState.IsValid) 37 | { 38 | var user = await _userManager.FindByEmailAsync(Input.Email); 39 | if (user == null || !(await _userManager.IsEmailConfirmedAsync(user))) 40 | { 41 | // Don't reveal that the user does not exist or is not confirmed 42 | return RedirectToPage("./ForgotPasswordConfirmation"); 43 | } 44 | 45 | // For more information on how to enable account confirmation and password reset please 46 | // visit https://go.microsoft.com/fwlink/?LinkID=532713 47 | var code = await _userManager.GeneratePasswordResetTokenAsync(user); 48 | var callbackUrl = Url.ResetPasswordCallbackLink(user.Id, code, Request.Scheme); 49 | await _emailSender.SendResetPasswordAsync(Input.Email, callbackUrl); 50 | return RedirectToPage("./ForgotPasswordConfirmation"); 51 | } 52 | 53 | return Page(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/ForgotPasswordConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ForgotPasswordConfirmation 3 | @{ 4 | ViewData["Title"] = "Forgot password confirmation"; 5 | } 6 | 7 |

@ViewData["Title"]

8 |

9 | Please check your email to reset your password. 10 |

11 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/ForgotPasswordConfirmation.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc.RazorPages; 6 | 7 | namespace RouteDebugging.Pages.Account 8 | { 9 | public class ForgotPasswordConfirmation : PageModel 10 | { 11 | public void OnGet() 12 | { 13 | 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Lockout.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model LockoutModel 3 | @{ 4 | ViewData["Title"] = "Locked out"; 5 | } 6 | 7 |
8 |

@ViewData["Title"]

9 |

This account has been locked out, please try again later.

10 |
11 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Lockout.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc.RazorPages; 6 | 7 | namespace RouteDebugging.Pages.Account 8 | { 9 | public class LockoutModel : PageModel 10 | { 11 | public void OnGet() 12 | { 13 | 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Login.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model LoginModel 3 | 4 | @{ 5 | ViewData["Title"] = "Log in"; 6 | } 7 | 8 |

@ViewData["Title"]

9 |
10 |
11 |
12 |
13 |

Use a local account to log in.

14 |
15 |
16 |
17 | 18 | 19 | 20 |
21 |
22 | 23 | 24 | 25 |
26 |
27 |
28 | 32 |
33 |
34 |
35 | 36 |
37 |
38 |

39 | Forgot your password? 40 |

41 |

42 | Register as a new user 43 |

44 |
45 |
46 |
47 |
48 |
49 |
50 |

Use another service to log in.

51 |
52 | @{ 53 | if ((Model.ExternalLogins?.Count ?? 0) == 0) 54 | { 55 |
56 |

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

60 |
61 | } 62 | else 63 | { 64 |
65 |
66 |

67 | @foreach (var provider in Model.ExternalLogins) 68 | { 69 | 70 | } 71 |

72 |
73 |
74 | } 75 | } 76 |
77 |
78 |
79 | 80 | @section Scripts { 81 | @await Html.PartialAsync("_ValidationScriptsPartial") 82 | } 83 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Login.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Authentication; 7 | using Microsoft.AspNetCore.Identity; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Microsoft.AspNetCore.Mvc.RazorPages; 10 | using Microsoft.Extensions.Logging; 11 | using RouteDebugging.Data; 12 | 13 | namespace RouteDebugging.Pages.Account 14 | { 15 | public class LoginModel : PageModel 16 | { 17 | private readonly SignInManager _signInManager; 18 | private readonly ILogger _logger; 19 | 20 | public LoginModel(SignInManager signInManager, ILogger logger) 21 | { 22 | _signInManager = signInManager; 23 | _logger = logger; 24 | } 25 | 26 | [BindProperty] 27 | public InputModel Input { get; set; } 28 | 29 | public IList ExternalLogins { get; set; } 30 | 31 | public string ReturnUrl { get; set; } 32 | 33 | [TempData] 34 | public string ErrorMessage { get; set; } 35 | 36 | public class InputModel 37 | { 38 | [Required] 39 | [EmailAddress] 40 | public string Email { get; set; } 41 | 42 | [Required] 43 | [DataType(DataType.Password)] 44 | public string Password { get; set; } 45 | 46 | [Display(Name = "Remember me?")] 47 | public bool RememberMe { get; set; } 48 | } 49 | 50 | public async Task OnGetAsync(string returnUrl = null) 51 | { 52 | if (!string.IsNullOrEmpty(ErrorMessage)) 53 | { 54 | ModelState.AddModelError(string.Empty, ErrorMessage); 55 | } 56 | 57 | // Clear the existing external cookie to ensure a clean login process 58 | await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); 59 | 60 | ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); 61 | 62 | ReturnUrl = returnUrl; 63 | } 64 | 65 | public async Task OnPostAsync(string returnUrl = null) 66 | { 67 | ReturnUrl = returnUrl; 68 | 69 | if (ModelState.IsValid) 70 | { 71 | // This doesn't count login failures towards account lockout 72 | // To enable password failures to trigger account lockout, set lockoutOnFailure: true 73 | var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true); 74 | if (result.Succeeded) 75 | { 76 | _logger.LogInformation("User logged in."); 77 | return LocalRedirect(Url.GetLocalUrl(returnUrl)); 78 | } 79 | if (result.RequiresTwoFactor) 80 | { 81 | return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe }); 82 | } 83 | if (result.IsLockedOut) 84 | { 85 | _logger.LogWarning("User account locked out."); 86 | return RedirectToPage("./Lockout"); 87 | } 88 | else 89 | { 90 | ModelState.AddModelError(string.Empty, "Invalid login attempt."); 91 | return Page(); 92 | } 93 | } 94 | 95 | // If we got this far, something failed, redisplay form 96 | return Page(); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/LoginWith2fa.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model LoginWith2faModel 3 | @{ 4 | ViewData["Title"] = "Two-factor authentication"; 5 | } 6 | 7 |

@ViewData["Title"]

8 |
9 |

Your login is protected with an authenticator app. Enter your authenticator code below.

10 |
11 |
12 |
13 | 14 |
15 |
16 | 17 | 18 | 19 |
20 |
21 |
22 | 26 |
27 |
28 |
29 | 30 |
31 |
32 |
33 |
34 |

35 | Don't have access to your authenticator device? You can 36 | log in with a recovery code. 37 |

38 | 39 | @section Scripts { 40 | @await Html.PartialAsync("_ValidationScriptsPartial") 41 | } -------------------------------------------------------------------------------- /SampleProject/Pages/Account/LoginWith2fa.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Identity; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.AspNetCore.Mvc.RazorPages; 9 | using Microsoft.Extensions.Logging; 10 | using RouteDebugging.Data; 11 | 12 | namespace RouteDebugging.Pages.Account 13 | { 14 | public class LoginWith2faModel : PageModel 15 | { 16 | private readonly SignInManager _signInManager; 17 | private readonly ILogger _logger; 18 | 19 | public LoginWith2faModel(SignInManager signInManager, ILogger logger) 20 | { 21 | _signInManager = signInManager; 22 | _logger = logger; 23 | } 24 | 25 | [BindProperty] 26 | public InputModel Input { get; set; } 27 | 28 | public bool RememberMe { get; set; } 29 | 30 | public string ReturnUrl { get; set; } 31 | 32 | public class InputModel 33 | { 34 | [Required] 35 | [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] 36 | [DataType(DataType.Text)] 37 | [Display(Name = "Authenticator code")] 38 | public string TwoFactorCode { get; set; } 39 | 40 | [Display(Name = "Remember this machine")] 41 | public bool RememberMachine { get; set; } 42 | } 43 | 44 | public async Task OnGetAsync(bool rememberMe, string returnUrl = null) 45 | { 46 | // Ensure the user has gone through the username & password screen first 47 | var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); 48 | 49 | if (user == null) 50 | { 51 | throw new ApplicationException($"Unable to load two-factor authentication user."); 52 | } 53 | 54 | ReturnUrl = returnUrl; 55 | RememberMe = rememberMe; 56 | 57 | return Page(); 58 | } 59 | 60 | public async Task OnPostAsync(bool rememberMe, string returnUrl = null) 61 | { 62 | if (!ModelState.IsValid) 63 | { 64 | return Page(); 65 | } 66 | 67 | var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); 68 | if (user == null) 69 | { 70 | throw new ApplicationException($"Unable to load two-factor authentication user."); 71 | } 72 | 73 | var authenticatorCode = Input.TwoFactorCode.Replace(" ", string.Empty).Replace("-", string.Empty); 74 | 75 | var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, rememberMe, Input.RememberMachine); 76 | 77 | if (result.Succeeded) 78 | { 79 | _logger.LogInformation("User with ID '{UserId}' logged in with 2fa.", user.Id); 80 | return LocalRedirect(Url.GetLocalUrl(returnUrl)); 81 | } 82 | else if (result.IsLockedOut) 83 | { 84 | _logger.LogWarning("User with ID '{UserId}' account locked out.", user.Id); 85 | return RedirectToPage("./Lockout"); 86 | } 87 | else 88 | { 89 | _logger.LogWarning("Invalid authenticator code entered for user with ID '{UserId}'.", user.Id); 90 | ModelState.AddModelError(string.Empty, "Invalid authenticator code."); 91 | return Page(); 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/LoginWithRecoveryCode.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model LoginWithRecoveryCodeModel 3 | @{ 4 | ViewData["Title"] = "Recovery code verification"; 5 | } 6 | 7 |

@ViewData["Title"]

8 |
9 |

10 | You have requested to log in with a recovery code. This login will not be remembered until you provide 11 | an authenticator app code at log in or disable 2FA and log in again. 12 |

13 |
14 |
15 |
16 |
17 |
18 | 19 | 20 | 21 |
22 | 23 |
24 |
25 |
26 | 27 | @section Scripts { 28 | @await Html.PartialAsync("_ValidationScriptsPartial") 29 | } -------------------------------------------------------------------------------- /SampleProject/Pages/Account/LoginWithRecoveryCode.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Identity; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.AspNetCore.Mvc.RazorPages; 9 | using Microsoft.Extensions.Logging; 10 | using RouteDebugging.Data; 11 | 12 | namespace RouteDebugging.Pages.Account 13 | { 14 | public class LoginWithRecoveryCodeModel : PageModel 15 | { 16 | private readonly SignInManager _signInManager; 17 | private readonly ILogger _logger; 18 | 19 | public LoginWithRecoveryCodeModel(SignInManager signInManager, ILogger logger) 20 | { 21 | _signInManager = signInManager; 22 | _logger = logger; 23 | } 24 | 25 | [BindProperty] 26 | public InputModel Input { get; set; } 27 | 28 | public string ReturnUrl { get; set; } 29 | 30 | public class InputModel 31 | { 32 | [BindProperty] 33 | [Required] 34 | [DataType(DataType.Text)] 35 | [Display(Name = "Recovery Code")] 36 | public string RecoveryCode { get; set; } 37 | } 38 | 39 | public async Task OnGetAsync(string returnUrl = null) 40 | { 41 | // Ensure the user has gone through the username & password screen first 42 | var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); 43 | if (user == null) 44 | { 45 | throw new ApplicationException($"Unable to load two-factor authentication user."); 46 | } 47 | 48 | ReturnUrl = returnUrl; 49 | 50 | return Page(); 51 | } 52 | 53 | public async Task OnPostAsync(string returnUrl = null) 54 | { 55 | if (!ModelState.IsValid) 56 | { 57 | return Page(); 58 | } 59 | 60 | var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); 61 | if (user == null) 62 | { 63 | throw new ApplicationException($"Unable to load two-factor authentication user."); 64 | } 65 | 66 | var recoveryCode = Input.RecoveryCode.Replace(" ", string.Empty); 67 | 68 | var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode); 69 | 70 | if (result.Succeeded) 71 | { 72 | _logger.LogInformation("User with ID '{UserId}' logged in with a recovery code.", user.Id); 73 | return LocalRedirect(Url.GetLocalUrl(returnUrl)); 74 | } 75 | if (result.IsLockedOut) 76 | { 77 | _logger.LogWarning("User with ID '{UserId}' account locked out.", user.Id); 78 | return RedirectToPage("./Lockout"); 79 | } 80 | else 81 | { 82 | _logger.LogWarning("Invalid recovery code entered for user with ID '{UserId}' ", user.Id); 83 | ModelState.AddModelError(string.Empty, "Invalid recovery code entered."); 84 | return Page(); 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/ChangePassword.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ChangePasswordModel 3 | @{ 4 | ViewData["Title"] = "Change password"; 5 | } 6 | 7 |

@ViewData["Title"]

8 | @Html.Partial("_StatusMessage", Model.StatusMessage) 9 |
10 |
11 |
12 |
13 |
14 | 15 | 16 | 17 |
18 |
19 | 20 | 21 | 22 |
23 |
24 | 25 | 26 | 27 |
28 | 29 |
30 |
31 |
32 | 33 | @section Scripts { 34 | @await Html.PartialAsync("_ValidationScriptsPartial") 35 | } 36 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/ChangePassword.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Identity; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.AspNetCore.Mvc.RazorPages; 9 | using Microsoft.Extensions.Logging; 10 | using RouteDebugging.Data; 11 | 12 | namespace RouteDebugging.Pages.Account.Manage 13 | { 14 | public class ChangePasswordModel : PageModel 15 | { 16 | private readonly UserManager _userManager; 17 | private readonly SignInManager _signInManager; 18 | private readonly ILogger _logger; 19 | 20 | public ChangePasswordModel( 21 | UserManager userManager, 22 | SignInManager signInManager, 23 | ILogger logger) 24 | { 25 | _userManager = userManager; 26 | _signInManager = signInManager; 27 | _logger = logger; 28 | } 29 | 30 | [BindProperty] 31 | public InputModel Input { get; set; } 32 | 33 | [TempData] 34 | public string StatusMessage { get; set; } 35 | 36 | public class InputModel 37 | { 38 | [Required] 39 | [DataType(DataType.Password)] 40 | [Display(Name = "Current password")] 41 | public string OldPassword { get; set; } 42 | 43 | [Required] 44 | [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] 45 | [DataType(DataType.Password)] 46 | [Display(Name = "New password")] 47 | public string NewPassword { get; set; } 48 | 49 | [DataType(DataType.Password)] 50 | [Display(Name = "Confirm new password")] 51 | [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] 52 | public string ConfirmPassword { get; set; } 53 | } 54 | 55 | public async Task OnGetAsync() 56 | { 57 | var user = await _userManager.GetUserAsync(User); 58 | if (user == null) 59 | { 60 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 61 | } 62 | 63 | var hasPassword = await _userManager.HasPasswordAsync(user); 64 | if (!hasPassword) 65 | { 66 | return RedirectToPage("./SetPassword"); 67 | } 68 | 69 | return Page(); 70 | } 71 | 72 | public async Task OnPostAsync() 73 | { 74 | if (!ModelState.IsValid) 75 | { 76 | return Page(); 77 | } 78 | 79 | var user = await _userManager.GetUserAsync(User); 80 | if (user == null) 81 | { 82 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 83 | } 84 | 85 | var changePasswordResult = await _userManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword); 86 | if (!changePasswordResult.Succeeded) 87 | { 88 | foreach (var error in changePasswordResult.Errors) 89 | { 90 | ModelState.AddModelError(string.Empty, error.Description); 91 | } 92 | return Page(); 93 | } 94 | 95 | await _signInManager.SignInAsync(user, isPersistent: false); 96 | _logger.LogInformation("User changed their password successfully."); 97 | StatusMessage = "Your password has been changed."; 98 | 99 | return RedirectToPage(); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/Disable2fa.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Disable2faModel 3 | @{ 4 | ViewData["Title"] = "Disable two-factor authentication (2FA)"; 5 | ViewData["ActivePage"] = "TwoFactorAuthentication"; 6 | } 7 | 8 |

@ViewData["Title"]

9 | 10 | 20 | 21 |
22 |
23 | 24 |
25 |
26 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/Disable2fa.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Identity; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.RazorPages; 8 | using Microsoft.Extensions.Logging; 9 | using RouteDebugging.Data; 10 | 11 | namespace RouteDebugging.Pages.Account.Manage 12 | { 13 | public class Disable2faModel : PageModel 14 | { 15 | private readonly UserManager _userManager; 16 | private readonly ILogger _logger; 17 | 18 | public Disable2faModel( 19 | UserManager userManager, 20 | ILogger logger) 21 | { 22 | _userManager = userManager; 23 | _logger = logger; 24 | } 25 | 26 | public async Task OnGet() 27 | { 28 | var user = await _userManager.GetUserAsync(User); 29 | if (user == null) 30 | { 31 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 32 | } 33 | 34 | if (!await _userManager.GetTwoFactorEnabledAsync(user)) 35 | { 36 | throw new ApplicationException($"Cannot disable 2FA for user with ID '{_userManager.GetUserId(User)}' as it's not currently enabled."); 37 | } 38 | 39 | return Page(); 40 | } 41 | 42 | public async Task OnPostAsync() 43 | { 44 | var user = await _userManager.GetUserAsync(User); 45 | if (user == null) 46 | { 47 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 48 | } 49 | 50 | var disable2faResult = await _userManager.SetTwoFactorEnabledAsync(user, false); 51 | if (!disable2faResult.Succeeded) 52 | { 53 | throw new ApplicationException($"Unexpected error occurred disabling 2FA for user with ID '{_userManager.GetUserId(User)}'."); 54 | } 55 | 56 | _logger.LogInformation("User with ID '{UserId}' has disabled 2fa.", _userManager.GetUserId(User)); 57 | 58 | return RedirectToPage("./TwoFactorAuthentication"); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/EnableAuthenticator.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model EnableAuthenticatorModel 3 | @{ 4 | ViewData["Title"] = "Configure authenticator app"; 5 | ViewData["ActivePage"] = "TwoFactorAuthentication"; 6 | } 7 | 8 |

@ViewData["Title"]

9 |
10 |

To use an authenticator app go through the following steps:

11 |
    12 |
  1. 13 |

    14 | Download a two-factor authenticator app like Microsoft Authenticator for 15 | Windows Phone, 16 | Android and 17 | iOS or 18 | Google Authenticator for 19 | Android and 20 | iOS. 21 |

    22 |
  2. 23 |
  3. 24 |

    Scan the QR Code or enter this key @Model.SharedKey into your two factor authenticator app. Spaces and casing do not matter.

    25 |
    To enable QR code generation please read our documentation.
    26 |
    27 |
    28 |
  4. 29 |
  5. 30 |

    31 | Once you have scanned the QR code or input the key above, your two factor authentication app will provide you 32 | with a unique code. Enter the code in the confirmation box below. 33 |

    34 |
    35 |
    36 |
    37 |
    38 | 39 | 40 | 41 |
    42 | 43 |
    44 |
    45 |
    46 |
    47 |
  6. 48 |
49 |
50 | 51 | @section Scripts { 52 | @await Html.PartialAsync("_ValidationScriptsPartial") 53 | } 54 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/EnableAuthenticator.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using System.Text.Encodings.Web; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using Microsoft.AspNetCore.Identity; 10 | using Microsoft.AspNetCore.Mvc; 11 | using Microsoft.AspNetCore.Mvc.RazorPages; 12 | using Microsoft.Extensions.Logging; 13 | using RouteDebugging.Data; 14 | 15 | namespace RouteDebugging.Pages.Account.Manage 16 | { 17 | public class EnableAuthenticatorModel : PageModel 18 | { 19 | private readonly UserManager _userManager; 20 | private readonly ILogger _logger; 21 | private readonly UrlEncoder _urlEncoder; 22 | 23 | private const string AuthenicatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6"; 24 | 25 | public EnableAuthenticatorModel( 26 | UserManager userManager, 27 | ILogger logger, 28 | UrlEncoder urlEncoder) 29 | { 30 | _userManager = userManager; 31 | _logger = logger; 32 | _urlEncoder = urlEncoder; 33 | } 34 | 35 | public string SharedKey { get; set; } 36 | 37 | public string AuthenticatorUri { get; set; } 38 | 39 | [BindProperty] 40 | public InputModel Input { get; set; } 41 | 42 | public class InputModel 43 | { 44 | [Required] 45 | [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] 46 | [DataType(DataType.Text)] 47 | [Display(Name = "Verification Code")] 48 | public string Code { get; set; } 49 | } 50 | 51 | public async Task OnGetAsync() 52 | { 53 | var user = await _userManager.GetUserAsync(User); 54 | if (user == null) 55 | { 56 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 57 | } 58 | 59 | await LoadSharedKeyAndQrCodeUriAsync(user); 60 | if (string.IsNullOrEmpty(SharedKey)) 61 | { 62 | await _userManager.ResetAuthenticatorKeyAsync(user); 63 | await LoadSharedKeyAndQrCodeUriAsync(user); 64 | } 65 | 66 | return Page(); 67 | } 68 | 69 | public async Task OnPostAsync() 70 | { 71 | var user = await _userManager.GetUserAsync(User); 72 | if (user == null) 73 | { 74 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 75 | } 76 | 77 | if (!ModelState.IsValid) 78 | { 79 | await LoadSharedKeyAndQrCodeUriAsync(user); 80 | return Page(); 81 | } 82 | 83 | // Strip spaces and hypens 84 | var verificationCode = Input.Code.Replace(" ", string.Empty).Replace("-", string.Empty); 85 | 86 | var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync( 87 | user, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode); 88 | 89 | if (!is2faTokenValid) 90 | { 91 | ModelState.AddModelError("Input.Code", "Verification code is invalid."); 92 | await LoadSharedKeyAndQrCodeUriAsync(user); 93 | return Page(); 94 | } 95 | 96 | await _userManager.SetTwoFactorEnabledAsync(user, true); 97 | _logger.LogInformation("User with ID '{UserId}' has enabled 2FA with an authenticator app.", user.Id); 98 | return RedirectToPage("./GenerateRecoveryCodes"); 99 | } 100 | 101 | private async Task LoadSharedKeyAndQrCodeUriAsync(ApplicationUser user) 102 | { 103 | // Load the authenticator key & QR code URI to display on the form 104 | var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); 105 | if (!string.IsNullOrEmpty(unformattedKey)) 106 | { 107 | SharedKey = FormatKey(unformattedKey); 108 | AuthenticatorUri = GenerateQrCodeUri(user.Email, unformattedKey); 109 | } 110 | } 111 | 112 | private string FormatKey(string unformattedKey) 113 | { 114 | var result = new StringBuilder(); 115 | int currentPosition = 0; 116 | while (currentPosition + 4 < unformattedKey.Length) 117 | { 118 | result.Append(unformattedKey.Substring(currentPosition, 4)).Append(" "); 119 | currentPosition += 4; 120 | } 121 | if (currentPosition < unformattedKey.Length) 122 | { 123 | result.Append(unformattedKey.Substring(currentPosition)); 124 | } 125 | 126 | return result.ToString().ToLowerInvariant(); 127 | } 128 | 129 | private string GenerateQrCodeUri(string email, string unformattedKey) 130 | { 131 | return string.Format( 132 | AuthenicatorUriFormat, 133 | _urlEncoder.Encode("RouteDebugging"), 134 | _urlEncoder.Encode(email), 135 | unformattedKey); 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/ExternalLogins.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ExternalLoginsModel 3 | @{ 4 | ViewData["Title"] = "Manage your external logins"; 5 | } 6 | 7 | @Html.Partial("_StatusMessage", Model.StatusMessage) 8 | @if (Model.CurrentLogins?.Count > 0) 9 | { 10 |

Registered Logins

11 | 12 | 13 | @foreach (var login in Model.CurrentLogins) 14 | { 15 | 16 | 17 | 33 | 34 | } 35 | 36 |
@login.LoginProvider 18 | @if (Model.ShowRemoveButton) 19 | { 20 |
21 |
22 | 23 | 24 | 25 |
26 |
27 | } 28 | else 29 | { 30 | @:   31 | } 32 |
37 | } 38 | @if (Model.OtherLogins?.Count > 0) 39 | { 40 |

Add another service to log in.

41 |
42 |
43 |
44 |

45 | @foreach (var provider in Model.OtherLogins) 46 | { 47 | 48 | } 49 |

50 |
51 |
52 | } 53 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/ExternalLogins.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Authentication; 6 | using Microsoft.AspNetCore.Identity; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.AspNetCore.Mvc.RazorPages; 9 | using RouteDebugging.Data; 10 | 11 | namespace RouteDebugging.Pages.Account.Manage 12 | { 13 | public class ExternalLoginsModel : PageModel 14 | { 15 | private readonly UserManager _userManager; 16 | private readonly SignInManager _signInManager; 17 | 18 | public ExternalLoginsModel( 19 | UserManager userManager, 20 | SignInManager signInManager) 21 | { 22 | _userManager = userManager; 23 | _signInManager = signInManager; 24 | } 25 | 26 | public IList CurrentLogins { get; set; } 27 | 28 | public IList OtherLogins { get; set; } 29 | 30 | public bool ShowRemoveButton { get; set; } 31 | 32 | [TempData] 33 | public string StatusMessage { get; set; } 34 | 35 | public async Task OnGetAsync() 36 | { 37 | var user = await _userManager.GetUserAsync(User); 38 | if (user == null) 39 | { 40 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 41 | } 42 | 43 | CurrentLogins = await _userManager.GetLoginsAsync(user); 44 | OtherLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()) 45 | .Where(auth => CurrentLogins.All(ul => auth.Name != ul.LoginProvider)) 46 | .ToList(); 47 | ShowRemoveButton = user.PasswordHash != null || CurrentLogins.Count > 1; 48 | return Page(); 49 | } 50 | 51 | public async Task OnPostRemoveLoginAsync(string loginProvider, string providerKey) 52 | { 53 | var user = await _userManager.GetUserAsync(User); 54 | if (user == null) 55 | { 56 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 57 | } 58 | 59 | var result = await _userManager.RemoveLoginAsync(user, loginProvider, providerKey); 60 | if (!result.Succeeded) 61 | { 62 | throw new ApplicationException($"Unexpected error occurred removing external login for user with ID '{user.Id}'."); 63 | } 64 | 65 | await _signInManager.SignInAsync(user, isPersistent: false); 66 | StatusMessage = "The external login was removed."; 67 | return RedirectToPage(); 68 | } 69 | 70 | public async Task OnPostLinkLoginAsync(string provider) 71 | { 72 | // Clear the existing external cookie to ensure a clean login process 73 | await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); 74 | 75 | // Request a redirect to the external login provider to link a login for the current user 76 | var redirectUrl = Url.Page("./ExternalLogins", pageHandler: "LinkLoginCallback"); 77 | var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User)); 78 | return new ChallengeResult(provider, properties); 79 | } 80 | 81 | public async Task OnGetLinkLoginCallbackAsync() 82 | { 83 | var user = await _userManager.GetUserAsync(User); 84 | if (user == null) 85 | { 86 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 87 | } 88 | 89 | var info = await _signInManager.GetExternalLoginInfoAsync(await _userManager.GetUserIdAsync(user)); 90 | if (info == null) 91 | { 92 | throw new ApplicationException($"Unexpected error occurred loading external login info for user with ID '{user.Id}'."); 93 | } 94 | 95 | var result = await _userManager.AddLoginAsync(user, info); 96 | if (!result.Succeeded) 97 | { 98 | throw new ApplicationException($"Unexpected error occurred adding external login for user with ID '{user.Id}'."); 99 | } 100 | 101 | // Clear the existing external cookie to ensure a clean login process 102 | await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); 103 | 104 | StatusMessage = "The external login was added."; 105 | return RedirectToPage(); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/GenerateRecoveryCodes.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model GenerateRecoveryCodesModel 3 | @{ 4 | ViewData["Title"] = "Recovery codes"; 5 | ViewData["ActivePage"] = "TwoFactorAuthentication"; 6 | } 7 | 8 |

@ViewData["Title"]

9 | 18 |
19 |
20 | @for (var row = 0; row < Model.RecoveryCodes.Count(); row += 2) 21 | { 22 | @Model.RecoveryCodes[row] @Model.RecoveryCodes[row + 1]
23 | } 24 |
25 |
-------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Identity; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.RazorPages; 8 | using Microsoft.Extensions.Logging; 9 | using RouteDebugging.Data; 10 | 11 | namespace RouteDebugging.Pages.Account.Manage 12 | { 13 | public class GenerateRecoveryCodesModel : PageModel 14 | { 15 | private readonly UserManager _userManager; 16 | private readonly ILogger _logger; 17 | 18 | public GenerateRecoveryCodesModel( 19 | UserManager userManager, 20 | ILogger logger) 21 | { 22 | _userManager = userManager; 23 | _logger = logger; 24 | } 25 | 26 | public string[] RecoveryCodes { get; set; } 27 | 28 | public async Task OnGetAsync() 29 | { 30 | var user = await _userManager.GetUserAsync(User); 31 | if (user == null) 32 | { 33 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 34 | } 35 | 36 | if (!user.TwoFactorEnabled) 37 | { 38 | throw new ApplicationException($"Cannot generate recovery codes for user with ID '{user.Id}' as they do not have 2FA enabled."); 39 | } 40 | 41 | var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); 42 | RecoveryCodes = recoveryCodes.ToArray(); 43 | 44 | _logger.LogInformation("User with ID '{UserId}' has generated new 2FA recovery codes.", user.Id); 45 | 46 | return Page(); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model IndexModel 3 | @{ 4 | ViewData["Title"] = "Profile"; 5 | } 6 | 7 |

@ViewData["Title"]

8 | @Html.Partial("_StatusMessage", Model.StatusMessage) 9 |
10 |
11 |
12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | @if (Model.IsEmailConfirmed) 20 | { 21 |
22 | 23 | 24 |
25 | } 26 | else 27 | { 28 | 29 | 30 | } 31 | 32 |
33 |
34 | 35 | 36 | 37 |
38 | 39 |
40 |
41 |
42 | 43 | @section Scripts { 44 | @await Html.PartialAsync("_ValidationScriptsPartial") 45 | } 46 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Text.Encodings.Web; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore.Identity; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Microsoft.AspNetCore.Mvc.RazorPages; 10 | using RouteDebugging.Data; 11 | using RouteDebugging.Services; 12 | 13 | namespace RouteDebugging.Pages.Account.Manage 14 | { 15 | public partial class IndexModel : PageModel 16 | { 17 | private readonly UserManager _userManager; 18 | private readonly SignInManager _signInManager; 19 | private readonly IEmailSender _emailSender; 20 | 21 | public IndexModel( 22 | UserManager userManager, 23 | SignInManager signInManager, 24 | IEmailSender emailSender) 25 | { 26 | _userManager = userManager; 27 | _signInManager = signInManager; 28 | _emailSender = emailSender; 29 | } 30 | 31 | public string Username { get; set; } 32 | 33 | public bool IsEmailConfirmed { get; set; } 34 | 35 | [TempData] 36 | public string StatusMessage { get; set; } 37 | 38 | [BindProperty] 39 | public InputModel Input { get; set; } 40 | 41 | public class InputModel 42 | { 43 | [Required] 44 | [EmailAddress] 45 | public string Email { get; set; } 46 | 47 | [Phone] 48 | [Display(Name = "Phone number")] 49 | public string PhoneNumber { get; set; } 50 | } 51 | 52 | public async Task OnGetAsync() 53 | { 54 | var user = await _userManager.GetUserAsync(User); 55 | if (user == null) 56 | { 57 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 58 | } 59 | 60 | Username = user.UserName; 61 | 62 | Input = new InputModel 63 | { 64 | Email = user.Email, 65 | PhoneNumber = user.PhoneNumber 66 | }; 67 | 68 | IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user); 69 | 70 | return Page(); 71 | } 72 | 73 | public async Task OnPostAsync() 74 | { 75 | if (!ModelState.IsValid) 76 | { 77 | return Page(); 78 | } 79 | 80 | var user = await _userManager.GetUserAsync(User); 81 | if (user == null) 82 | { 83 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 84 | } 85 | 86 | if (Input.Email != user.Email) 87 | { 88 | var setEmailResult = await _userManager.SetEmailAsync(user, Input.Email); 89 | if (!setEmailResult.Succeeded) 90 | { 91 | throw new ApplicationException($"Unexpected error occurred setting email for user with ID '{user.Id}'."); 92 | } 93 | } 94 | 95 | if (Input.PhoneNumber != user.PhoneNumber) 96 | { 97 | var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, Input.PhoneNumber); 98 | if (!setPhoneResult.Succeeded) 99 | { 100 | throw new ApplicationException($"Unexpected error occurred setting phone number for user with ID '{user.Id}'."); 101 | } 102 | } 103 | 104 | StatusMessage = "Your profile has been updated"; 105 | return RedirectToPage(); 106 | } 107 | public async Task OnPostSendVerificationEmailAsync() 108 | { 109 | if (!ModelState.IsValid) 110 | { 111 | return Page(); 112 | } 113 | 114 | var user = await _userManager.GetUserAsync(User); 115 | if (user == null) 116 | { 117 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 118 | } 119 | 120 | var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); 121 | var callbackUrl = Url.EmailConfirmationLink(user.Id, code, Request.Scheme); 122 | await _emailSender.SendEmailConfirmationAsync(user.Email, callbackUrl); 123 | 124 | StatusMessage = "Verification email sent. Please check your email."; 125 | return RedirectToPage(); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/ManageNavPages.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc.Rendering; 6 | 7 | namespace RouteDebugging.Pages.Account.Manage 8 | { 9 | public static class ManageNavPages 10 | { 11 | public static string Index => "Index"; 12 | 13 | public static string ChangePassword => "ChangePassword"; 14 | 15 | public static string ExternalLogins => "ExternalLogins"; 16 | 17 | public static string TwoFactorAuthentication => "TwoFactorAuthentication"; 18 | 19 | public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index); 20 | 21 | public static string ChangePasswordNavClass(ViewContext viewContext) => PageNavClass(viewContext, ChangePassword); 22 | 23 | public static string ExternalLoginsNavClass(ViewContext viewContext) => PageNavClass(viewContext, ExternalLogins); 24 | 25 | public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication); 26 | 27 | public static string PageNavClass(ViewContext viewContext, string page) 28 | { 29 | var activePage = viewContext.ViewData["ActivePage"] as string 30 | ?? System.IO.Path.GetFileNameWithoutExtension(viewContext.ActionDescriptor.DisplayName); 31 | return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : null; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/ResetAuthenticator.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ResetAuthenticatorModel 3 | @{ 4 | ViewData["Title"] = "Reset authenticator key"; 5 | ViewData["ActivePage"] = "TwoFactorAuthentication"; 6 | } 7 | 8 |

@ViewData["Title"]

9 | 19 |
20 |
21 | 22 |
23 |
-------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/ResetAuthenticator.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Identity; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.RazorPages; 8 | using Microsoft.Extensions.Logging; 9 | using RouteDebugging.Data; 10 | 11 | namespace RouteDebugging.Pages.Account.Manage 12 | { 13 | public class ResetAuthenticatorModel : PageModel 14 | { 15 | UserManager _userManager; 16 | ILogger _logger; 17 | 18 | public ResetAuthenticatorModel( 19 | UserManager userManager, 20 | ILogger logger) 21 | { 22 | _userManager = userManager; 23 | _logger = logger; 24 | } 25 | public async Task OnGet() 26 | { 27 | var user = await _userManager.GetUserAsync(User); 28 | if (user == null) 29 | { 30 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 31 | } 32 | 33 | return Page(); 34 | } 35 | 36 | public async Task OnPostAsync() 37 | { 38 | var user = await _userManager.GetUserAsync(User); 39 | if (user == null) 40 | { 41 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 42 | } 43 | 44 | await _userManager.SetTwoFactorEnabledAsync(user, false); 45 | await _userManager.ResetAuthenticatorKeyAsync(user); 46 | _logger.LogInformation("User with ID '{UserId}' has reset their authentication app key.", user.Id); 47 | 48 | return RedirectToPage("./EnableAuthenticator"); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/SetPassword.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model SetPasswordModel 3 | @{ 4 | ViewData["Title"] = "Set password"; 5 | ViewData["ActivePage"] = "ChangePassword"; 6 | } 7 | 8 |

Set your password

9 | @Html.Partial("_StatusMessage", Model.StatusMessage) 10 |

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

14 |
15 |
16 |
17 |
18 |
19 | 20 | 21 | 22 |
23 |
24 | 25 | 26 | 27 |
28 | 29 |
30 |
31 |
32 | 33 | @section Scripts { 34 | @await Html.PartialAsync("_ValidationScriptsPartial") 35 | } 36 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/SetPassword.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Identity; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.AspNetCore.Mvc.RazorPages; 9 | using RouteDebugging.Data; 10 | 11 | namespace RouteDebugging.Pages.Account.Manage 12 | { 13 | public class SetPasswordModel : PageModel 14 | { 15 | private readonly UserManager _userManager; 16 | private readonly SignInManager _signInManager; 17 | 18 | public SetPasswordModel( 19 | UserManager userManager, 20 | SignInManager signInManager) 21 | { 22 | _userManager = userManager; 23 | _signInManager = signInManager; 24 | } 25 | 26 | [BindProperty] 27 | public InputModel Input { get; set; } 28 | 29 | [TempData] 30 | public string StatusMessage { get; set; } 31 | 32 | public class InputModel 33 | { 34 | [Required] 35 | [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] 36 | [DataType(DataType.Password)] 37 | [Display(Name = "New password")] 38 | public string NewPassword { get; set; } 39 | 40 | [DataType(DataType.Password)] 41 | [Display(Name = "Confirm new password")] 42 | [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] 43 | public string ConfirmPassword { get; set; } 44 | } 45 | 46 | public async Task OnGetAsync() 47 | { 48 | var user = await _userManager.GetUserAsync(User); 49 | if (user == null) 50 | { 51 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 52 | } 53 | 54 | var hasPassword = await _userManager.HasPasswordAsync(user); 55 | 56 | if (hasPassword) 57 | { 58 | return RedirectToPage("./ChangePassword"); 59 | } 60 | 61 | return Page(); 62 | } 63 | 64 | public async Task OnPostAsync() 65 | { 66 | if (!ModelState.IsValid) 67 | { 68 | return Page(); 69 | } 70 | 71 | var user = await _userManager.GetUserAsync(User); 72 | if (user == null) 73 | { 74 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 75 | } 76 | 77 | var addPasswordResult = await _userManager.AddPasswordAsync(user, Input.NewPassword); 78 | if (!addPasswordResult.Succeeded) 79 | { 80 | foreach (var error in addPasswordResult.Errors) 81 | { 82 | ModelState.AddModelError(string.Empty, error.Description); 83 | } 84 | return Page(); 85 | } 86 | 87 | await _signInManager.SignInAsync(user, isPersistent: false); 88 | StatusMessage = "Your password has been set."; 89 | 90 | return RedirectToPage(); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/TwoFactorAuthentication.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model TwoFactorAuthenticationModel 3 | @{ 4 | ViewData["Title"] = "Two-factor authentication (2FA)"; 5 | } 6 | 7 |

@ViewData["Title"]

8 | @if (Model.Is2faEnabled) 9 | { 10 | if (Model.RecoveryCodesLeft == 0) 11 | { 12 |
13 | You have no recovery codes left. 14 |

You must generate a new set of recovery codes before you can log in with a recovery code.

15 |
16 | } 17 | else if (Model.RecoveryCodesLeft == 1) 18 | { 19 |
20 | You have 1 recovery code left. 21 |

You can generate a new set of recovery codes.

22 |
23 | } 24 | else if (Model.RecoveryCodesLeft <= 3) 25 | { 26 |
27 | You have @Model.RecoveryCodesLeft recovery codes left. 28 |

You should generate a new set of recovery codes.

29 |
30 | } 31 | 32 | Disable 2FA 33 | Reset recovery codes 34 | } 35 | 36 |
Authenticator app
37 | @if (!Model.HasAuthenticator) 38 | { 39 | Add authenticator app 40 | } 41 | else 42 | { 43 | Configure authenticator app 44 | Reset authenticator app 45 | } 46 | 47 | @section Scripts { 48 | @await Html.PartialAsync("_ValidationScriptsPartial") 49 | } 50 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Identity; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.RazorPages; 8 | using Microsoft.Extensions.Logging; 9 | using RouteDebugging.Data; 10 | 11 | namespace RouteDebugging.Pages.Account.Manage 12 | { 13 | public class TwoFactorAuthenticationModel : PageModel 14 | { 15 | private const string AuthenicatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}"; 16 | 17 | private readonly UserManager _userManager; 18 | private readonly SignInManager _signInManager; 19 | private readonly ILogger _logger; 20 | 21 | public TwoFactorAuthenticationModel( 22 | UserManager userManager, 23 | SignInManager signInManager, 24 | ILogger logger) 25 | { 26 | _userManager = userManager; 27 | _signInManager = signInManager; 28 | _logger = logger; 29 | } 30 | 31 | public bool HasAuthenticator { get; set; } 32 | 33 | public int RecoveryCodesLeft { get; set; } 34 | 35 | [BindProperty] 36 | public bool Is2faEnabled { get; set; } 37 | 38 | public async Task OnGet() 39 | { 40 | var user = await _userManager.GetUserAsync(User); 41 | if (user == null) 42 | { 43 | throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 44 | } 45 | 46 | HasAuthenticator = await _userManager.GetAuthenticatorKeyAsync(user) != null; 47 | Is2faEnabled = await _userManager.GetTwoFactorEnabledAsync(user); 48 | RecoveryCodesLeft = await _userManager.CountRecoveryCodesAsync(user); 49 | 50 | return Page(); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "/Pages/_Layout.cshtml"; 3 | } 4 | 5 |

Manage your account

6 | 7 |
8 |

Change your account settings

9 |
10 |
11 |
12 | @await Html.PartialAsync("_ManageNav") 13 |
14 |
15 | @RenderBody() 16 |
17 |
18 |
19 | 20 | @section Scripts { 21 | @RenderSection("Scripts", required: false) 22 | } 23 | 24 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/_ManageNav.cshtml: -------------------------------------------------------------------------------- 1 | @inject SignInManager SignInManager 2 | @{ 3 | var hasExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).Any(); 4 | } 5 | 6 | 15 | 16 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/_StatusMessage.cshtml: -------------------------------------------------------------------------------- 1 | @model string 2 | 3 | @if (!String.IsNullOrEmpty(Model)) 4 | { 5 | var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success"; 6 | 10 | } 11 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Manage/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using RouteDebugging.Pages.Account.Manage 2 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Register.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model RegisterModel 3 | @{ 4 | ViewData["Title"] = "Register"; 5 | } 6 | 7 |

@ViewData["Title"]

8 | 9 |
10 |
11 |
12 |

Create a new account.

13 |
14 |
15 |
16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 |
25 |
26 | 27 | 28 | 29 |
30 | 31 |
32 |
33 |
34 | 35 | @section Scripts { 36 | @await Html.PartialAsync("_ValidationScriptsPartial") 37 | } 38 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/Register.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Text.Encodings.Web; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Identity; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.AspNetCore.Mvc.RazorPages; 9 | using Microsoft.Extensions.Logging; 10 | using RouteDebugging.Data; 11 | using RouteDebugging.Services; 12 | 13 | namespace RouteDebugging.Pages.Account 14 | { 15 | public class RegisterModel : PageModel 16 | { 17 | private readonly SignInManager _signInManager; 18 | private readonly UserManager _userManager; 19 | private readonly ILogger _logger; 20 | private readonly IEmailSender _emailSender; 21 | 22 | public RegisterModel( 23 | UserManager userManager, 24 | SignInManager signInManager, 25 | ILogger logger, 26 | IEmailSender emailSender) 27 | { 28 | _userManager = userManager; 29 | _signInManager = signInManager; 30 | _logger = logger; 31 | _emailSender = emailSender; 32 | } 33 | 34 | [BindProperty] 35 | public InputModel Input { get; set; } 36 | 37 | public string ReturnUrl { get; set; } 38 | 39 | public class InputModel 40 | { 41 | [Required] 42 | [EmailAddress] 43 | [Display(Name = "Email")] 44 | public string Email { get; set; } 45 | 46 | [Required] 47 | [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] 48 | [DataType(DataType.Password)] 49 | [Display(Name = "Password")] 50 | public string Password { get; set; } 51 | 52 | [DataType(DataType.Password)] 53 | [Display(Name = "Confirm password")] 54 | [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] 55 | public string ConfirmPassword { get; set; } 56 | } 57 | 58 | public void OnGet(string returnUrl = null) 59 | { 60 | ReturnUrl = returnUrl; 61 | } 62 | 63 | public async Task OnPostAsync(string returnUrl = null) 64 | { 65 | ReturnUrl = returnUrl; 66 | if (ModelState.IsValid) 67 | { 68 | var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email }; 69 | var result = await _userManager.CreateAsync(user, Input.Password); 70 | if (result.Succeeded) 71 | { 72 | _logger.LogInformation("User created a new account with password."); 73 | 74 | var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); 75 | var callbackUrl = Url.EmailConfirmationLink(user.Id, code, Request.Scheme); 76 | await _emailSender.SendEmailConfirmationAsync(Input.Email, callbackUrl); 77 | 78 | await _signInManager.SignInAsync(user, isPersistent: false); 79 | return LocalRedirect(Url.GetLocalUrl(returnUrl)); 80 | } 81 | foreach (var error in result.Errors) 82 | { 83 | ModelState.AddModelError(string.Empty, error.Description); 84 | } 85 | } 86 | 87 | // If we got this far, something failed, redisplay form 88 | return Page(); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/ResetPassword.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ResetPasswordModel 3 | @{ 4 | ViewData["Title"] = "Reset password"; 5 | } 6 | 7 |

@ViewData["Title"]

8 |

Reset your password.

9 |
10 |
11 |
12 |
13 |
14 | 15 |
16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 |
25 |
26 | 27 | 28 | 29 |
30 | 31 |
32 |
33 |
34 | 35 | @section Scripts { 36 | @await Html.PartialAsync("_ValidationScriptsPartial") 37 | } 38 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/ResetPassword.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Identity; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.AspNetCore.Mvc.RazorPages; 9 | using RouteDebugging.Data; 10 | 11 | namespace RouteDebugging.Pages.Account 12 | { 13 | public class ResetPasswordModel : PageModel 14 | { 15 | private readonly UserManager _userManager; 16 | 17 | public ResetPasswordModel(UserManager userManager) 18 | { 19 | _userManager = userManager; 20 | } 21 | 22 | [BindProperty] 23 | public InputModel Input { get; set; } 24 | 25 | public class InputModel 26 | { 27 | [Required] 28 | [EmailAddress] 29 | public string Email { get; set; } 30 | 31 | [Required] 32 | [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] 33 | [DataType(DataType.Password)] 34 | public string Password { get; set; } 35 | 36 | [DataType(DataType.Password)] 37 | [Display(Name = "Confirm password")] 38 | [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] 39 | public string ConfirmPassword { get; set; } 40 | 41 | public string Code { get; set; } 42 | } 43 | 44 | public IActionResult OnGet(string code = null) 45 | { 46 | if (code == null) 47 | { 48 | throw new ApplicationException("A code must be supplied for password reset."); 49 | } 50 | else 51 | { 52 | Input = new InputModel 53 | { 54 | Code = code 55 | }; 56 | return Page(); 57 | } 58 | } 59 | 60 | public async Task OnPostAsync() 61 | { 62 | if (!ModelState.IsValid) 63 | { 64 | return Page(); 65 | } 66 | 67 | var user = await _userManager.FindByEmailAsync(Input.Email); 68 | if (user == null) 69 | { 70 | // Don't reveal that the user does not exist 71 | return RedirectToPage("./ResetPasswordConfirmation"); 72 | } 73 | 74 | var result = await _userManager.ResetPasswordAsync(user, Input.Code, Input.Password); 75 | if (result.Succeeded) 76 | { 77 | return RedirectToPage("./ResetPasswordConfirmation"); 78 | } 79 | 80 | foreach (var error in result.Errors) 81 | { 82 | ModelState.AddModelError(string.Empty, error.Description); 83 | } 84 | return Page(); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/ResetPasswordConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ResetPasswordConfirmationModel 3 | @{ 4 | ViewData["Title"] = "Reset password confirmation"; 5 | } 6 | 7 |

@ViewData["Title"]

8 |

9 | Your password has been reset. Please click here to log in. 10 |

11 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/ResetPasswordConfirmation.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc.RazorPages; 6 | 7 | namespace RouteDebugging.Pages.Account 8 | { 9 | public class ResetPasswordConfirmationModel : PageModel 10 | { 11 | public void OnGet() 12 | { 13 | 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/SignedOut.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model SignedOutModel 3 | @{ 4 | ViewData["Title"] = "Signed out"; 5 | } 6 | 7 |

@ViewData["Title"]

8 |

9 | You have successfully signed out. 10 |

11 | -------------------------------------------------------------------------------- /SampleProject/Pages/Account/SignedOut.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | public class SignedOutModel : PageModel 9 | { 10 | public IActionResult OnGet() 11 | { 12 | if (User.Identity.IsAuthenticated) 13 | { 14 | // Redirect to home page if the user is authenticated. 15 | return RedirectToPage("/Index"); 16 | } 17 | 18 | return Page(); 19 | } 20 | } -------------------------------------------------------------------------------- /SampleProject/Pages/Account/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using RouteDebugging.Pages.Account 2 | -------------------------------------------------------------------------------- /SampleProject/Pages/Contact.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ContactModel 3 | @{ 4 | ViewData["Title"] = "Contact"; 5 | } 6 |

@ViewData["Title"]

7 |

@Model.Message

8 | 9 |
10 | One Microsoft Way
11 | Redmond, WA 98052-6399
12 | P: 13 | 425.555.0100 14 |
15 | 16 |
17 | Support: Support@example.com
18 | Marketing: Marketing@example.com 19 |
20 | -------------------------------------------------------------------------------- /SampleProject/Pages/Contact.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc.RazorPages; 6 | 7 | namespace RouteDebugging.Pages 8 | { 9 | public class ContactModel : PageModel 10 | { 11 | public string Message { get; set; } 12 | 13 | public void OnGet() 14 | { 15 | Message = "Your contact page."; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /SampleProject/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ErrorModel 3 | @{ 4 | ViewData["Title"] = "Error"; 5 | } 6 | 7 |

Error.

8 |

An error occurred while processing your request.

9 | 10 | @if (Model.ShowRequestId) 11 | { 12 |

13 | Request ID: @Model.RequestId 14 |

15 | } 16 | 17 |

Development Mode

18 |

19 | Swapping to Development environment will display more detailed information about the error that occurred. 20 |

21 |

22 | Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. 23 |

24 | -------------------------------------------------------------------------------- /SampleProject/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace RouteDebugging.Pages 9 | { 10 | public class ErrorModel : PageModel 11 | { 12 | public string RequestId { get; set; } 13 | 14 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 15 | 16 | public void OnGet() 17 | { 18 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SampleProject/Pages/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model IndexModel 3 | @{ 4 | ViewData["Title"] = "Home page"; 5 | } 6 | 7 | View Routes 8 | -------------------------------------------------------------------------------- /SampleProject/Pages/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace RouteDebugging.Pages 9 | { 10 | public class IndexModel : PageModel 11 | { 12 | public void OnGet() 13 | { 14 | 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SampleProject/Pages/Routes.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model RouteDebugging.Pages.RoutesModel 3 | @{ 4 | ViewData["Title"] = "Routes"; 5 | } 6 | 7 |

@ViewData["Title"]

8 |

Route Debug Info by @@ardalis

9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | @foreach (var route in Model.Routes) 23 | { 24 | @if (!String.IsNullOrEmpty(route.Template)) 25 | { 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | } 35 | } 36 | 37 |
Route TemplateControllerActionConstraints/VerbsRouteValuesName
@route.Template@route.Controller@route.Action@route.Constraint@route.RouteValues@route.Name
38 | -------------------------------------------------------------------------------- /SampleProject/Pages/Routes.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.RazorPages; 2 | using Microsoft.AspNetCore.Mvc.Infrastructure; 3 | using Microsoft.AspNetCore.Mvc.Controllers; 4 | using System.Text.Json; 5 | 6 | namespace RouteDebugging.Pages; 7 | 8 | public class RoutesModel : PageModel 9 | { 10 | private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider; 11 | 12 | public RoutesModel(IActionDescriptorCollectionProvider actionDescriptorCollectionProvider) 13 | { 14 | this._actionDescriptorCollectionProvider = actionDescriptorCollectionProvider; 15 | } 16 | 17 | public List Routes { get; set; } = new(); 18 | 19 | public void OnGet() 20 | { 21 | // DisplayName 22 | var PageRoutes = _actionDescriptorCollectionProvider.ActionDescriptors.Items.OfType() 23 | .Select(x => new RouteInfo 24 | { 25 | Action = "", 26 | Controller = x.DisplayName ?? string.Empty, 27 | Name = x.AttributeRouteInfo?.Name ?? string.Empty, 28 | Template = x.AttributeRouteInfo?.Template ?? string.Empty, 29 | Constraint = x.ActionConstraints == null ? "" : JsonSerializer.Serialize(x.ActionConstraints), 30 | RouteValues = string.Join(',', x.RouteValues) 31 | }) 32 | .OrderBy(r => r.Template) 33 | .ToList(); 34 | 35 | var ViewRoutes = _actionDescriptorCollectionProvider.ActionDescriptors.Items.OfType() 36 | .Select(x => new RouteInfo 37 | { 38 | Action = x.RouteValues["Action"] ?? string.Empty, 39 | Controller = x.RouteValues["Controller"] ?? string.Empty, 40 | Name = x.AttributeRouteInfo?.Name ?? string.Empty, 41 | Template = x.AttributeRouteInfo?.Template ?? string.Empty, 42 | Constraint = x.ActionConstraints == null ? "" : JsonSerializer.Serialize(x.ActionConstraints), 43 | }) 44 | .OrderBy(r => r.Template) 45 | .ToList(); 46 | 47 | Routes = PageRoutes.Concat(ViewRoutes).ToList(); 48 | } 49 | 50 | public class RouteInfo 51 | { 52 | public string Template { get; set; } = string.Empty; 53 | public string Name { get; set; } = string.Empty; 54 | public string Controller { get; set; } = string.Empty; 55 | public string Action { get; set; } = string.Empty; 56 | public string Constraint { get; set; } = string.Empty; 57 | public string RouteValues { get; set; } = string.Empty; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /SampleProject/Pages/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @ViewData["Title"] - RouteDebugging 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 41 |
42 | @RenderBody() 43 |
44 |
45 |

© @DateTime.Today.Year - RouteDebugging

46 |
47 |
48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 61 | 67 | 68 | 69 | 70 | @RenderSection("Scripts", required: false) 71 | 72 | 73 | -------------------------------------------------------------------------------- /SampleProject/Pages/_LoginPartial.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Identity 2 | @using RouteDebugging.Data 3 | @inject SignInManager SignInManager 4 | @inject UserManager UserManager 5 | 6 | @if (SignInManager.IsSignedIn(User)) 7 | { 8 | 18 | } 19 | else 20 | { 21 | 25 | } 26 | -------------------------------------------------------------------------------- /SampleProject/Pages/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /SampleProject/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Identity 2 | @using RouteDebugging 3 | @using RouteDebugging.Data 4 | @namespace RouteDebugging.Pages 5 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 6 | -------------------------------------------------------------------------------- /SampleProject/Pages/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /SampleProject/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity; 2 | using Microsoft.EntityFrameworkCore; 3 | using RouteDebugging.Data; 4 | using RouteDebugging.Services; 5 | 6 | var builder = WebApplication.CreateBuilder(args); 7 | 8 | builder.Services.AddRazorPages(options => 9 | { 10 | options.Conventions.AuthorizeFolder("/Account/Manage"); 11 | options.Conventions.AuthorizePage("/Account/Logout"); 12 | }); 13 | builder.Services.AddMvc(); 14 | 15 | builder.Services.AddDbContext(options => 16 | options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); 17 | 18 | builder.Services.AddIdentity() 19 | .AddEntityFrameworkStores() 20 | .AddDefaultTokenProviders(); 21 | 22 | builder.Services.AddSingleton(); 23 | 24 | var app = builder.Build(); 25 | 26 | app.UseRouting(); 27 | app.UseAuthorization(); 28 | app.MapStaticAssets(); 29 | 30 | app.MapControllerRoute( 31 | name: "default", 32 | pattern: "{controller=Home}/{action=Index}/{id?}"); 33 | app.MapRazorPages(); 34 | 35 | app.Run(); 36 | 37 | -------------------------------------------------------------------------------- /SampleProject/RouteDebugging.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /SampleProject/Services/EmailSender.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace RouteDebugging.Services 7 | { 8 | // This class is used by the application to send email for account confirmation and password reset. 9 | // For more details see https://go.microsoft.com/fwlink/?LinkID=532713 10 | public class EmailSender : IEmailSender 11 | { 12 | public Task SendEmailAsync(string email, string subject, string message) 13 | { 14 | return Task.CompletedTask; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SampleProject/Services/IEmailSender.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace RouteDebugging.Services 7 | { 8 | public interface IEmailSender 9 | { 10 | Task SendEmailAsync(string email, string subject, string message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SampleProject/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /SampleProject/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-RouteDebugging-53bc9b9d-9d6a-45d4-8429-2a2761773502;Trusted_Connection=True;MultipleActiveResultSets=true" 4 | }, 5 | "Logging": { 6 | "LogLevel": { 7 | "Default": "Information", 8 | "Microsoft.AspNetCore": "Warning" 9 | } 10 | }, 11 | "AllowedHosts": "*" 12 | } 13 | -------------------------------------------------------------------------------- /SampleProject/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Wrapping element */ 7 | /* Set some basic padding to keep content from hitting the edges */ 8 | .body-content { 9 | padding-left: 15px; 10 | padding-right: 15px; 11 | } 12 | 13 | /* Carousel */ 14 | .carousel-caption p { 15 | font-size: 20px; 16 | line-height: 1.4; 17 | } 18 | 19 | /* Make .svg files in the carousel display properly in older browsers */ 20 | .carousel-inner .item img[src$=".svg"] { 21 | width: 100%; 22 | } 23 | 24 | /* QR code generator */ 25 | #qrCode { 26 | margin: 15px; 27 | } 28 | 29 | /* Hide/rearrange for smaller screens */ 30 | @media screen and (max-width: 767px) { 31 | /* Hide captions */ 32 | .carousel-caption { 33 | display: none; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /SampleProject/wwwroot/css/site.min.css: -------------------------------------------------------------------------------- 1 | body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}#qrCode{margin:15px}@media screen and (max-width:767px){.carousel-caption{display:none}} -------------------------------------------------------------------------------- /SampleProject/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ardalis/AspNetCoreRouteDebugger/1345ce3aaa2921552319a3efff7cba2b1ec49967/SampleProject/wwwroot/favicon.ico -------------------------------------------------------------------------------- /SampleProject/wwwroot/images/banner1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SampleProject/wwwroot/images/banner2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SampleProject/wwwroot/images/banner3.svg: -------------------------------------------------------------------------------- 1 | banner3b -------------------------------------------------------------------------------- /SampleProject/wwwroot/images/banner4.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SampleProject/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Write your Javascript code. 2 | -------------------------------------------------------------------------------- /SampleProject/wwwroot/js/site.min.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ardalis/AspNetCoreRouteDebugger/1345ce3aaa2921552319a3efff7cba2b1ec49967/SampleProject/wwwroot/js/site.min.js -------------------------------------------------------------------------------- /SampleProject/wwwroot/lib/bootstrap/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootstrap", 3 | "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", 4 | "keywords": [ 5 | "css", 6 | "js", 7 | "less", 8 | "mobile-first", 9 | "responsive", 10 | "front-end", 11 | "framework", 12 | "web" 13 | ], 14 | "homepage": "http://getbootstrap.com", 15 | "license": "MIT", 16 | "moduleType": "globals", 17 | "main": [ 18 | "less/bootstrap.less", 19 | "dist/js/bootstrap.js" 20 | ], 21 | "ignore": [ 22 | "/.*", 23 | "_config.yml", 24 | "CNAME", 25 | "composer.json", 26 | "CONTRIBUTING.md", 27 | "docs", 28 | "js/tests", 29 | "test-infra" 30 | ], 31 | "dependencies": { 32 | "jquery": "1.9.1 - 3" 33 | }, 34 | "version": "3.3.7", 35 | "_release": "3.3.7", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.3.7", 39 | "commit": "0b9c4a4007c44201dce9a6cc1a38407005c26c86" 40 | }, 41 | "_source": "https://github.com/twbs/bootstrap.git", 42 | "_target": "v3.3.7", 43 | "_originalSource": "bootstrap", 44 | "_direct": true 45 | } -------------------------------------------------------------------------------- /SampleProject/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2016 Twitter, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /SampleProject/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ardalis/AspNetCoreRouteDebugger/1345ce3aaa2921552319a3efff7cba2b1ec49967/SampleProject/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /SampleProject/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ardalis/AspNetCoreRouteDebugger/1345ce3aaa2921552319a3efff7cba2b1ec49967/SampleProject/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /SampleProject/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ardalis/AspNetCoreRouteDebugger/1345ce3aaa2921552319a3efff7cba2b1ec49967/SampleProject/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /SampleProject/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ardalis/AspNetCoreRouteDebugger/1345ce3aaa2921552319a3efff7cba2b1ec49967/SampleProject/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /SampleProject/wwwroot/lib/bootstrap/dist/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /SampleProject/wwwroot/lib/jquery-validation-unobtrusive/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation-unobtrusive", 3 | "version": "3.2.6", 4 | "homepage": "https://github.com/aspnet/jquery-validation-unobtrusive", 5 | "description": "Add-on to jQuery Validation to enable unobtrusive validation options in data-* attributes.", 6 | "main": [ 7 | "jquery.validate.unobtrusive.js" 8 | ], 9 | "ignore": [ 10 | "**/.*", 11 | "*.json", 12 | "*.md", 13 | "*.txt", 14 | "gulpfile.js" 15 | ], 16 | "keywords": [ 17 | "jquery", 18 | "asp.net", 19 | "mvc", 20 | "validation", 21 | "unobtrusive" 22 | ], 23 | "authors": [ 24 | "Microsoft" 25 | ], 26 | "license": "http://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm", 27 | "repository": { 28 | "type": "git", 29 | "url": "git://github.com/aspnet/jquery-validation-unobtrusive.git" 30 | }, 31 | "dependencies": { 32 | "jquery-validation": ">=1.8", 33 | "jquery": ">=1.8" 34 | }, 35 | "_release": "3.2.6", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.2.6", 39 | "commit": "13386cd1b5947d8a5d23a12b531ce3960be1eba7" 40 | }, 41 | "_source": "git://github.com/aspnet/jquery-validation-unobtrusive.git", 42 | "_target": "3.2.6", 43 | "_originalSource": "jquery-validation-unobtrusive" 44 | } -------------------------------------------------------------------------------- /SampleProject/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Unobtrusive validation support library for jQuery and jQuery Validate 3 | ** Copyright (C) Microsoft Corporation. All rights reserved. 4 | */ 5 | !function(a){function e(a,e,n){a.rules[e]=n,a.message&&(a.messages[e]=a.message)}function n(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function t(a){return a.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function r(a){return a.substr(0,a.lastIndexOf(".")+1)}function i(a,e){return 0===a.indexOf("*.")&&(a=a.replace("*.",e)),a}function o(e,n){var r=a(this).find("[data-valmsg-for='"+t(n[0].name)+"']"),i=r.attr("data-valmsg-replace"),o=i?a.parseJSON(i)!==!1:null;r.removeClass("field-validation-valid").addClass("field-validation-error"),e.data("unobtrusiveContainer",r),o?(r.empty(),e.removeClass("input-validation-error").appendTo(r)):e.hide()}function d(e,n){var t=a(this).find("[data-valmsg-summary=true]"),r=t.find("ul");r&&r.length&&n.errorList.length&&(r.empty(),t.addClass("validation-summary-errors").removeClass("validation-summary-valid"),a.each(n.errorList,function(){a("
  • ").html(this.message).appendTo(r)}))}function s(e){var n=e.data("unobtrusiveContainer");if(n){var t=n.attr("data-valmsg-replace"),r=t?a.parseJSON(t):null;n.addClass("field-validation-valid").removeClass("field-validation-error"),e.removeData("unobtrusiveContainer"),r&&n.empty()}}function l(e){var n=a(this),t="__jquery_unobtrusive_validation_form_reset";if(!n.data(t)){n.data(t,!0);try{n.data("validator").resetForm()}finally{n.removeData(t)}n.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors"),n.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}}function m(e){var n=a(e),t=n.data(v),r=a.proxy(l,e),i=p.unobtrusive.options||{},m=function(n,t){var r=i[n];r&&a.isFunction(r)&&r.apply(e,t)};return t||(t={options:{errorClass:i.errorClass||"input-validation-error",errorElement:i.errorElement||"span",errorPlacement:function(){o.apply(e,arguments),m("errorPlacement",arguments)},invalidHandler:function(){d.apply(e,arguments),m("invalidHandler",arguments)},messages:{},rules:{},success:function(){s.apply(e,arguments),m("success",arguments)}},attachValidation:function(){n.off("reset."+v,r).on("reset."+v,r).validate(this.options)},validate:function(){return n.validate(),n.valid()}},n.data(v,t)),t}var u,p=a.validator,v="unobtrusiveValidation";p.unobtrusive={adapters:[],parseElement:function(e,n){var t,r,i,o=a(e),d=o.parents("form")[0];d&&(t=m(d),t.options.rules[e.name]=r={},t.options.messages[e.name]=i={},a.each(this.adapters,function(){var n="data-val-"+this.name,t=o.attr(n),s={};void 0!==t&&(n+="-",a.each(this.params,function(){s[this]=o.attr(n+this)}),this.adapt({element:e,form:d,message:t,params:s,rules:r,messages:i}))}),a.extend(r,{__dummy__:!0}),n||t.attachValidation())},parse:function(e){var n=a(e),t=n.parents().addBack().filter("form").add(n.find("form")).has("[data-val=true]");n.find("[data-val=true]").each(function(){p.unobtrusive.parseElement(this,!0)}),t.each(function(){var a=m(this);a&&a.attachValidation()})}},u=p.unobtrusive.adapters,u.add=function(a,e,n){return n||(n=e,e=[]),this.push({name:a,params:e,adapt:n}),this},u.addBool=function(a,n){return this.add(a,function(t){e(t,n||a,!0)})},u.addMinMax=function(a,n,t,r,i,o){return this.add(a,[i||"min",o||"max"],function(a){var i=a.params.min,o=a.params.max;i&&o?e(a,r,[i,o]):i?e(a,n,i):o&&e(a,t,o)})},u.addSingleVal=function(a,n,t){return this.add(a,[n||"val"],function(r){e(r,t||a,r.params[n])})},p.addMethod("__dummy__",function(a,e,n){return!0}),p.addMethod("regex",function(a,e,n){var t;return this.optional(e)?!0:(t=new RegExp(n).exec(a),t&&0===t.index&&t[0].length===a.length)}),p.addMethod("nonalphamin",function(a,e,n){var t;return n&&(t=a.match(/\W/g),t=t&&t.length>=n),t}),p.methods.extension?(u.addSingleVal("accept","mimtype"),u.addSingleVal("extension","extension")):u.addSingleVal("extension","extension","accept"),u.addSingleVal("regex","pattern"),u.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"),u.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range"),u.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength"),u.add("equalto",["other"],function(n){var o=r(n.element.name),d=n.params.other,s=i(d,o),l=a(n.form).find(":input").filter("[name='"+t(s)+"']")[0];e(n,"equalTo",l)}),u.add("required",function(a){("INPUT"!==a.element.tagName.toUpperCase()||"CHECKBOX"!==a.element.type.toUpperCase())&&e(a,"required",!0)}),u.add("remote",["url","type","additionalfields"],function(o){var d={url:o.params.url,type:o.params.type||"GET",data:{}},s=r(o.element.name);a.each(n(o.params.additionalfields||o.element.name),function(e,n){var r=i(n,s);d.data[r]=function(){var e=a(o.form).find(":input").filter("[name='"+t(r)+"']");return e.is(":checkbox")?e.filter(":checked").val()||e.filter(":hidden").val()||"":e.is(":radio")?e.filter(":checked").val()||"":e.val()}}),e(o,"remote",d)}),u.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&e(a,"minlength",a.params.min),a.params.nonalphamin&&e(a,"nonalphamin",a.params.nonalphamin),a.params.regex&&e(a,"regex",a.params.regex)}),a(function(){p.unobtrusive.parse(document)})}(jQuery); -------------------------------------------------------------------------------- /SampleProject/wwwroot/lib/jquery-validation/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation", 3 | "homepage": "http://jqueryvalidation.org/", 4 | "repository": { 5 | "type": "git", 6 | "url": "git://github.com/jzaefferer/jquery-validation.git" 7 | }, 8 | "authors": [ 9 | "Jörn Zaefferer " 10 | ], 11 | "description": "Form validation made easy", 12 | "main": "dist/jquery.validate.js", 13 | "keywords": [ 14 | "forms", 15 | "validation", 16 | "validate" 17 | ], 18 | "license": "MIT", 19 | "ignore": [ 20 | "**/.*", 21 | "node_modules", 22 | "bower_components", 23 | "test", 24 | "demo", 25 | "lib" 26 | ], 27 | "dependencies": { 28 | "jquery": ">= 1.7.2" 29 | }, 30 | "version": "1.14.0", 31 | "_release": "1.14.0", 32 | "_resolution": { 33 | "type": "version", 34 | "tag": "1.14.0", 35 | "commit": "c1343fb9823392aa9acbe1c3ffd337b8c92fed48" 36 | }, 37 | "_source": "git://github.com/jzaefferer/jquery-validation.git", 38 | "_target": ">=1.8", 39 | "_originalSource": "jquery-validation" 40 | } -------------------------------------------------------------------------------- /SampleProject/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /SampleProject/wwwroot/lib/jquery/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery", 3 | "main": "dist/jquery.js", 4 | "license": "MIT", 5 | "ignore": [ 6 | "package.json" 7 | ], 8 | "keywords": [ 9 | "jquery", 10 | "javascript", 11 | "browser", 12 | "library" 13 | ], 14 | "homepage": "https://github.com/jquery/jquery-dist", 15 | "version": "2.2.0", 16 | "_release": "2.2.0", 17 | "_resolution": { 18 | "type": "version", 19 | "tag": "2.2.0", 20 | "commit": "6fc01e29bdad0964f62ef56d01297039cdcadbe5" 21 | }, 22 | "_source": "git://github.com/jquery/jquery-dist.git", 23 | "_target": "2.2.0", 24 | "_originalSource": "jquery" 25 | } -------------------------------------------------------------------------------- /SampleProject/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright jQuery Foundation and other contributors, https://jquery.org/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | --------------------------------------------------------------------------------