Conversation
Add missing Identity services, authentication middleware, Razor Pages endpoints, login navigation, and an initial EF Core migration. Add end-to-end coverage for clean MVC and Razor Pages projects, runtime endpoints, and repeated scaffolding. Fixes #3831 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c90ed2f5-6b56-45f7-8157-d3fa618a7ffc
Verify that a second scaffolding pass leaves MVC and Razor Pages projects created with the default Identity UI unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c90ed2f5-6b56-45f7-8157-d3fa618a7ffc
Update the regular Identity scaffolder output to match the default Identity UI behavior and markup in dotnet/aspnetcore release/11.0. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c90ed2f5-6b56-45f7-8157-d3fa618a7ffc
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c90ed2f5-6b56-45f7-8157-d3fa618a7ffc
Apply the generated migration and verify registration, email confirmation, login, account management, logout, and Identity static assets. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c90ed2f5-6b56-45f7-8157-d3fa618a7ffc
There was a problem hiding this comment.
🟡 Changes recommended
Four moderate issues remain in Identity registration detection, navbar insertion, layout integration, and external-login navigation.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Configures ASP.NET Core Identity for MVC and Razor Pages, adds migration and navigation support, updates .NET 11 templates, and expands integration coverage.
Changes:
- Adds Identity host configuration, navigation, and EF migration generation.
- Aligns .NET 11 Identity templates with the default UI.
- Expands unit, integration, and end-to-end tests.
File summaries
| File | Summary |
|---|---|
test/dotnet-scaffolding/dotnet-scaffold.Tests/AspNet/ScaffoldSteps/ConfigureIdentityNavigationStepTests.cs |
Navigation tests |
test/dotnet-scaffolding/dotnet-scaffold.Tests/AspNet/ScaffoldSteps/AddIdentityMigrationStepTests.cs |
Migration tests |
test/dotnet-scaffolding/dotnet-scaffold.Tests/AspNet/Integration/Identity/IdentityNet11IntegrationTests.cs |
.NET 11 integration tests |
test/dotnet-scaffolding/dotnet-scaffold.Tests/AspNet/Integration/Identity/IdentityIntegrationTestsBase.cs |
Identity test infrastructure |
test/dotnet-scaffolding/dotnet-scaffold.Tests/AspNet/Integration/Identity/IdentityEndToEndNet10Tests.cs |
End-to-end lifecycle tests |
test/dotnet-scaffolding/dotnet-scaffold.Tests/AspNet/Extensions/IdentityScaffolderBuilderExtensionsTests.cs |
Builder extension tests |
src/dotnet-scaffolding/dotnet-scaffold/dotnet-scaffold.csproj |
Packaging updates |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net8.0/CodeModificationConfigs/identityChanges.json |
.NET 8 Identity configuration |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/ResetPasswordModel.tt |
Reset password template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/ResetPasswordModel.cs |
Reset password output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/ResendEmailConfirmationModel.tt |
Resend confirmation template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/ResendEmailConfirmationModel.cs |
Resend confirmation output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/RegisterModel.tt |
Registration model template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/RegisterModel.cs |
Registration model output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/RegisterConfirmationModel.tt |
Registration confirmation template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/RegisterConfirmationModel.cs |
Registration confirmation output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Register.tt |
Registration page template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Register.cs |
Registration page output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/SetPassword.tt |
Set-password template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/SetPassword.cs |
Set-password output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/PersonalData.tt |
Personal-data template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/PersonalData.cs |
Personal-data output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/ManageNavPagesModel.tt |
Manage navigation model template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/ManageNavPagesModel.cs |
Manage navigation model output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/Index.tt |
Manage index template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/Index.cs |
Manage index output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/ExternalLogins.tt |
External logins template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/ExternalLogins.cs |
External logins output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/EnableAuthenticator.tt |
Authenticator template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/EnableAuthenticator.cs |
Authenticator output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/EmailModel.tt |
Email model template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/EmailModel.cs |
Email model output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/Email.tt |
Email page template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/Email.cs |
Email page output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/ChangePassword.tt |
Password-change template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/ChangePassword.cs |
Password-change output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/_ManageNav.tt |
Manage navigation template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/_ManageNav.cs |
Manage navigation output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/_Layout.tt |
Manage layout template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/_Layout.cs |
Manage layout output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/LogoutModel.tt |
Logout model template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/LogoutModel.cs |
Logout model output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/LoginWithRecoveryCodeModel.tt |
Recovery-code template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/LoginWithRecoveryCodeModel.cs |
Recovery-code output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/LoginWith2faModel.tt |
Two-factor model template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/LoginWith2faModel.cs |
Two-factor model output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/LoginModel.tt |
Login model template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/LoginModel.cs |
Login model output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Login.tt |
Login page template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Login.cs |
Login page output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/ForgotPasswordModel.tt |
Password-recovery template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/ForgotPasswordModel.cs |
Password-recovery output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/ExternalLoginModel.tt |
External-login template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/ExternalLoginModel.cs |
External-login output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/ConfirmEmailModel.tt |
Email-confirmation template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/ConfirmEmailModel.cs |
Email-confirmation output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/ConfirmEmailChangeModel.tt |
Email-change template |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/ConfirmEmailChangeModel.cs |
Email-change output |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/CodeModificationConfigs/identityChanges.json |
.NET 11 Identity configuration |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net10.0/CodeModificationConfigs/identityChanges.json |
.NET 10 Identity configuration |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/ScaffoldSteps/ConfigureIdentityNavigationStep.cs |
Navigation integration |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/ScaffoldSteps/AddIdentityMigrationStep.cs |
Migration generation |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Extensions/IdentityScaffolderBuilderExtensions.cs |
Identity pipeline registration |
src/dotnet-scaffolding/dotnet-scaffold/AspNet/AspNetCommandService.cs |
Scaffold pipeline integration |
Review details
Suppressed comments (3)
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Extensions/IdentityScaffolderBuilderExtensions.cs:178
- This only recognizes registrations written with the literal
builder.Services.Add...prefix. A valid existing setup such asvar services = builder.Services; services.AddIdentity<...>()(or a line-broken/aliasedAddDefaultIdentity) is treated as absent, so the code-modification step inserts a second Identity registration. That violates the no-duplicate/preserve-customization requirement; detect the registration structurally (or use the same code-analysis mechanism as the modifier) rather than relying on this exact text.
if (programContent.Contains(registration, StringComparison.Ordinal))
src/dotnet-scaffolding/dotnet-scaffold/AspNet/ScaffoldSteps/ConfigureIdentityNavigationStep.cs:42
- When
_LoginPartial.cshtmlalready exists but is not referenced by this layout (for example, a project-owned partial in Shared), this early return prevents adding the layout reference, so the generated Login/Register links are never reachable. The existing partial should be preserved, but layout configuration must continue unless the layout already references it.
if (layoutContent.Contains("_LoginPartial", StringComparison.OrdinalIgnoreCase))
{
return Task.FromResult(true);
src/dotnet-scaffolding/dotnet-scaffold/AspNet/Templates/net11.0/Identity/Pages/Account/Manage/_ManageNav.tt:11
_ManageNavnow testsViewData["ManageNav.HasExternalLogins"], but none of the generated Identity templates set that key (the previous implementation computedhasExternalLoginsfromSignInManager). As a result, applications with configured external providers will silently omit the External logins management link. Either restore the localSignInManagerquery or add the correspondingViewDatainitialization to the manage layout/pages before rendering this partial.
- Files reviewed: 64/65 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| var closingListIndex = openingListIndex < 0 | ||
| ? -1 | ||
| : layoutContent.IndexOf("</ul>", navbarClassIndex, StringComparison.OrdinalIgnoreCase); |
Apply the generated migration through an isolated runner project so CI output path settings do not break dotnet-ef on macOS. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c90ed2f5-6b56-45f7-8157-d3fa618a7ffc
Exercise the generated migrations endpoint so the lifecycle test avoids platform-specific dotnet-ef output paths and secondary Razor builds. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c90ed2f5-6b56-45f7-8157-d3fa618a7ffc
Find the closing ul that matches the navbar list before inserting the Identity login partial, with regression coverage for nested dropdown lists. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c90ed2f5-6b56-45f7-8157-d3fa618a7ffc
Reuse Roslyn and MSBuild project analysis, preserve existing Identity configuration, and generate host navigation through the template pipeline. Fix project-relative output paths, missing appsettings creation, and .NET 11 no-op email-sender detection. Consolidate SDK-pinned MVC and Razor Pages lifecycle coverage, require canonical generated pages, and verify unchanged second scaffolding runs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7f8f84a2-809d-43db-b74e-4f4f888dfd4c
Summary
Fully configure local Identity for MVC and Razor Pages, preserve existing configuration, and make repeated scaffolding a source-file no-op.
appsettings.jsonwhen it is missing.Fixes #3831
Cleanup and scope
The builder extensions configure steps rather than inspect source files. Existing project-analysis, code-modification, and templating infrastructure is reused. Navigation editing is separate from file generation, and one login-partial template is shared across supported target frameworks.
The end-to-end suite shares project/process setup and uses the matching installed SDK for each target framework. It requires generated pages at their canonical paths, so passing requests cannot conceal incorrectly placed pages by falling back to the packaged UI.
.NET 8 retains a separate host-modification configuration because
WithStaticAssets()is unavailable there. No Blazor Identity templates are changed; the new user-type and migration analysis is gated to regular Identity..NET 11 Identity UI comparison
The template baseline remains
dotnet/aspnetcore@13909f3b. Differences are limited to scaffolded application types/namespaces, host layout integration, and detecting the framework's internal typed email adapter through its type name and public legacy sender.Validation