{"id":46797,"date":"2023-08-08T10:06:00","date_gmt":"2023-08-08T17:06:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=46797"},"modified":"2023-09-18T14:53:59","modified_gmt":"2023-09-18T21:53:59","slug":"asp-net-core-updates-in-dotnet-8-preview-7","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/asp-net-core-updates-in-dotnet-8-preview-7\/","title":{"rendered":"ASP.NET Core updates in .NET 8 Preview 7"},"content":{"rendered":"<p><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-dotnet-8-preview-7\">.NET 8 Preview 7 is now available<\/a> and includes many great new improvements to ASP.NET Core.<\/p>\n<p>Here&#8217;s a summary of what&#8217;s new in this preview release:<\/p>\n<ul>\n<li><a href=\"#servers\">Servers &amp; middleware<\/a>\n<ul>\n<li>Antiforgery middleware<\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#apis\">API authoring<\/a>\n<ul>\n<li>Antiforgery integration for minimal APIs<\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#native-aot\">Native AOT<\/a>\n<ul>\n<li>Request Delegate Generator supports interceptors feature<\/li>\n<li>Full TrimMode is used for web projects compiled with trimming enabled<\/li>\n<li><code>WebApplication.CreateEmptyBuilder<\/code><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#blazor\">Blazor<\/a>\n<ul>\n<li>Antiforgery integration<\/li>\n<li>Server-side form handling improvements<\/li>\n<li>Auto render mode<\/li>\n<li>Register root-level cascading values<\/li>\n<li>Improved integration of interactive components with server-side rendering<\/li>\n<li>New <code>EmptyContent<\/code> parameter for <code>Virtualize<\/code><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#identity\">Identity<\/a>\n<ul>\n<li>New bearer token authentication handler<\/li>\n<li>New API endpoints<\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#spa\">Single page apps (SPA)<\/a>\n<ul>\n<li>New Visual Studio templates<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>For more details on the ASP.NET Core work planned for .NET 8 see the full <a href=\"https:\/\/aka.ms\/aspnet\/roadmap\">ASP.NET Core roadmap for .NET 8<\/a> on GitHub.<\/p>\n<h2>Get started<\/h2>\n<p>To get started with ASP.NET Core in .NET 8 Preview 7, <a href=\"https:\/\/dotnet.microsoft.com\/next\">install the .NET 8 SDK<\/a>.<\/p>\n<p>If you&#8217;re on Windows using Visual Studio, we recommend installing the latest <a href=\"https:\/\/visualstudio.com\/preview\">Visual Studio 2022 preview<\/a>. If you&#8217;re using Visual Studio Code, you can try out the new <a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/announcing-csharp-dev-kit-for-visual-studio-code\/\">C# Dev Kit<\/a>. If you are on macOS, you can now develop using Visual Studio for Mac 17.6.1 after enabling the preview feature for .NET 8 in Preferences.<\/p>\n<h2>Upgrade an existing project<\/h2>\n<p>To upgrade an existing ASP.NET Core app from .NET 8 Preview 6 to .NET 8 Preview 7:<\/p>\n<ul>\n<li>Update the target framework of your app to <code>net8.0<\/code>.<\/li>\n<li>Update all Microsoft.AspNetCore.* package references to <code>8.0.0-preview.7.*<\/code>.<\/li>\n<li>Update all Microsoft.Extensions.* package references to <code>8.0.0-preview.7.*<\/code>.<\/li>\n<\/ul>\n<p>See also the full list of <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/compatibility\/8.0#aspnet-core\">breaking changes<\/a> in ASP.NET Core for .NET 8.<\/p>\n<p><a id=\"servers\"><\/a><\/p>\n<h2>Servers &amp; middleware<\/h2>\n<h3>Antiforgery middleware<\/h3>\n<p>This release adds a middleware for validating antiforgery tokens, which are used to mitigate cross-site request forgery attacks. When antiforgery services are registered via the <code>AddAntiforgery<\/code> method, the antiforgery middleware is automatically enabled in the the target application.<\/p>\n<pre><code class=\"language-csharp\">var builder = WebApplication.CreateBuilder();\n\nbuilder.Services.AddAntiforgery();\n\nvar app = builder.Build();\n\n\/\/ Implicitly added by WebApplicationBuilder\n\/\/ app.UseAntiforgery();\n\napp.Run();<\/code><\/pre>\n<p>The antiforgery middleware itself does <em>not<\/em> short-circuit the execution of the rest of the request pipeline. Instead the middleware sets the <code>IAntiforgeryValidationFeature<\/code> in the <code>HttpContext.Features<\/code> of the current request. The middleware expects that each framework implementation (minimal APIs, MVC, Blazor, etc.) will react to this feature and short-circuit execution as expected.<\/p>\n<p>The antiforgery token is only validated if:<\/p>\n<ul>\n<li>The endpoint contains metadata implementing <code>IAntiforgeryMetadata<\/code> where <code>RequiresValidation=true<\/code>.<\/li>\n<li>The HTTP method associated with the endpoint is a relevant HTTP method (not TRACE, OPTIONS, HEAD, GET).<\/li>\n<li>The request is associated with a valid endpoint.<\/li>\n<\/ul>\n<p>Note that the antiforgery middleware must run <em>after<\/em> the authentication and authorization middleware to avoid inadvertently reading form data when the user is unauthenticated.<\/p>\n<p><a id=\"apis\"><\/a><\/p>\n<h2>API authoring<\/h2>\n<h3>Antiforgery integration for minimal APIs<\/h3>\n<p>Minimal APIs that accept form data now require antiforgery token validation by default.<\/p>\n<p>In the code sample below:<\/p>\n<ul>\n<li>Antiforgery services are registered in DI so the antiforgery middleware is automatically enabled.<\/li>\n<li>Two endpoints are provided for displaying a form: <code>\/antiforgery<\/code> which renders a form with a hidden antiforgery token field and <code>\/no-antiforgery<\/code> which render a form without the antiforgery token field.<\/li>\n<li>The <code>\/todo<\/code> endpoint processes a <code>Todo<\/code> object from the form and will automatically require antiforgery token validation.<\/li>\n<\/ul>\n<pre><code class=\"language-csharp\">using Microsoft.AspNetCore.Antiforgery;\nusing Microsoft.AspNetCore.Mvc;\n\nvar builder = WebApplication.CreateBuilder();\n\nbuilder.Services.AddAntiforgery();\n\nvar app = builder.Build();\n\napp.MapGet(\"\/antiforgery\", (HttpContext context, IAntiforgery antiforgery) =&gt;\n{\n    var token = antiforgery.GetAndStoreTokens(context);\n    var html = $\"\"\"\n        &lt;html&gt;\n            &lt;body&gt;\n                &lt;form action=\"\/todo\" method=\"POST\" enctype=\"multipart\/form-data\"&gt;\n                    &lt;input name=\"{token.FormFieldName}\" type=\"hidden\" value=\"{token.RequestToken}\" \/&gt;\n                    &lt;input type=\"text\" name=\"name\" \/&gt;\n                    &lt;input type=\"date\" name=\"dueDate\" \/&gt;\n                    &lt;input type=\"checkbox\" name=\"isCompleted\" \/&gt;\n                    &lt;input type=\"submit\" \/&gt;\n                &lt;\/form&gt;\n            &lt;\/body&gt;\n        &lt;\/html&gt;\n    \"\"\";\n    return Results.Content(html, \"text\/html\");\n});\n\napp.MapGet(\"\/no-antiforgery\", () =&gt;\n{\n    var html = \"\"\"\n        &lt;html&gt;\n            &lt;body&gt;\n                &lt;form action=\"\/todo\" method=\"POST\" enctype=\"multipart\/form-data\"&gt;\n                    &lt;input type=\"text\" name=\"name\" \/&gt;\n                    &lt;input type=\"date\" name=\"dueDate\" \/&gt;\n                    &lt;input type=\"checkbox\" name=\"isCompleted\" \/&gt;\n                    &lt;input type=\"submit\" \/&gt;\n                &lt;\/form&gt;\n            &lt;\/body&gt;\n        &lt;\/html&gt;\n    \"\"\";\n    return Results.Content(html, \"text\/html\");\n});\n\napp.MapPost(\"\/todo\", ([FromForm] Todo todo) =&gt; Results.Ok(todo));\n\napp.Run();\n\nclass Todo\n{\n    public string Name { get; set; }\n    public bool IsCompleted { get; set; }\n    public DateTime DueDate { get; set; }\n}<\/code><\/pre>\n<p>submitting the form at <code>\/antiforgery<\/code> will result in a successful response. On the other hand, submitting the form at <code>\/no-antiforgery<\/code> will produce an exception at runtime because no valid antiforgery token was presented. In <code>Production<\/code> environments, this will produce a log instead of throwing an exception:<\/p>\n<pre><code class=\"language-console\">Microsoft.AspNetCore.Http.BadHttpRequestException: Invalid antiforgery token found when reading parameter \"Todo todo\" from the request body as form.\nAn unhandled exception has occurred while executing the request.\nMicrosoft.AspNetCore.Http.BadHttpRequestException: Invalid antiforgery token found when reading parameter \"Todo todo\" from the request body as form.\n ---&gt; Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException: The required antiforgery request token was not provided in either form field \"__RequestVerificationToken\" or header value \"RequestVerificationToken\".\n   at Microsoft.AspNetCore.Antiforgery.DefaultAntiforgery.ValidateRequestAsync(HttpContext httpContext)\n   at Microsoft.AspNetCore.Antiforgery.Internal.AntiforgeryMiddleware.InvokeAwaited(HttpContext context)<\/code><\/pre>\n<p><a id=\"native-aot\"><\/a><\/p>\n<h2>Native AOT<\/h2>\n<h3>Request Delegate Generator supports interceptors feature<\/h3>\n<p>The <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/asp-net-core-updates-in-dotnet-8-preview-3#minimal-apis-and-native-aot\">Request Delegate Generator<\/a> introduced in .NET 8 Preview 3 has been updated to use the new <a href=\"https:\/\/learn.microsoft.com\/dotnet\/csharp\/whats-new\/csharp-12#interceptors\">C# 12 interceptors<\/a> compiler feature to support intercepting calls to minimal API&#8217;s <code>Map<\/code> action methods with statically generated variants at runtime. As a result of this change, users can expect to see improvements in startup performance for apps compiled with <code>PublishAot<\/code> enabled.<\/p>\n<p>The table below outlines the changes in startup time &#8211; the time it takes for routing to process all endpoints in an application &#8211; across three scenarios:<\/p>\n<ul>\n<li>The baseline, where <code>RequestDelegate<\/code>s for endpoints are generated at runtime using reflection and dynamic code generation.<\/li>\n<li>Endpoints generated at compile-time without the interceptors feature, the default between Preview 3 and Preview 6.<\/li>\n<li>Endpoints generated at compile-time with the interceptors feature, the default in Preview 7.<\/li>\n<\/ul>\n<table>\n<thead>\n<tr>\n<th>Iteration<\/th>\n<th>Baseline (runtime-timed generated endpoints)<\/th>\n<th>Request Delegate Generator (without interceptors)<\/th>\n<th>Request Delegate Generator (with interceptors)<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>1<\/td>\n<td>716.2313ms<\/td>\n<td>372.5172ms<\/td>\n<td>31.5255ms<\/td>\n<\/tr>\n<tr>\n<td>2<\/td>\n<td>747.279ms<\/td>\n<td>355.1435ms<\/td>\n<td>64.188ms<\/td>\n<\/tr>\n<tr>\n<td>3<\/td>\n<td>730.975ms<\/td>\n<td>350.5353ms<\/td>\n<td>32.9315ms<\/td>\n<\/tr>\n<tr>\n<td>4<\/td>\n<td>729.2775ms<\/td>\n<td>345.8684ms<\/td>\n<td>34.1169ms<\/td>\n<\/tr>\n<tr>\n<td>5<\/td>\n<td>711.0555ms<\/td>\n<td>351.2683ms<\/td>\n<td>38.7152ms<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2023\/09\/request-delegate-generator-with-interceptors.svg\" alt=\"Chart showing Request Delegate Generator with interceptors performance\" \/><\/p>\n<h3>Full TrimMode is used for web projects compiled with trimming enabled<\/h3>\n<p>In this preview, we introduce a <em>breaking change<\/em> that will impact Web projects compiled with trimming enabled via <code>PublishTrimmed=true<\/code>. Prior to this release, projects used the <code>partial<\/code> TrimMode by default. Moving forward, <code>TrimMode=full<\/code> will be enabled for all projects that target .NET 8 or above. For more information on this breaking change, see <a href=\"https:\/\/github.com\/aspnet\/Announcements\/issues\/507\">the announcement<\/a>.<\/p>\n<h3><code>WebApplication.CreateEmptyBuilder<\/code><\/h3>\n<p>We&#8217;ve added a new <code>WebApplicationBuilder<\/code> factory method for building small apps that only contain necessary features: <code>WebApplication.CreateEmptyBuilder(WebApplicationOptions options)<\/code>. This <code>WebApplicationBuilder<\/code> is created with no built-in behavior. Your app will contain only the services and middleware that you configure.<\/p>\n<p>Here&#8217;s an example of using this API to create a small web application:<\/p>\n<pre><code class=\"language-C#\">var builder = WebApplication.CreateEmptyBuilder(new WebApplicationOptions());\nbuilder.WebHost.UseKestrelCore();\n\nvar app = builder.Build();\n\napp.Use(async (context, next) =&gt;\n{\n    await context.Response.WriteAsync(\"Hello, World!\");\n    await next(context);\n});\n\nConsole.WriteLine(\"Running...\");\napp.Run();<\/code><\/pre>\n<p>Publishing this code with native AOT using .NET 8 Preview 7 on a linux-x64 machine results in a self-contained, native executable of about <code>8.5 MB<\/code>.<\/p>\n<p><a id=\"blazor\"><\/a><\/p>\n<h2>Blazor<\/h2>\n<h3>Antiforgery integration<\/h3>\n<p>Blazor endpoints now require antiforgery protection by default. You can enable antiforgery support using the new antiforgery middleware and using the <code>AntiforgeryToken<\/code> component to generate tokens for rendered forms. The <code>EditForm<\/code> component will add the antiforgery token automatically for you.<\/p>\n<p>Use the <code>[RequireAntiforgeryToken]<\/code> attribute in a Blazor component page to indicate if the component requires antiforgery projection or not. For example, you can disable the antiforgery requirement for a page (not recommended!) like this:<\/p>\n<pre><code class=\"language-razor\">@using Microsoft.AspNetCore.Antiforgery;\n@attribute [RequireAntiforgeryToken(required: false)]<\/code><\/pre>\n<h3>Server-side form handling improvements<\/h3>\n<p>You can now build standard HTML forms in Blazor when using server-side rendering and without using <code>EditForm<\/code>. Create a form using the normal HTML <code>form<\/code> tag and specify an <code>@onsubmit<\/code> handler for handling the submitted form request.<\/p>\n<pre><code class=\"language-razor\">&lt;form method=\"post\" @formname=\"contact\" @onsubmit=\"AddContact\"&gt;\n    &lt;div&gt;\n        &lt;label for=\"name\"&gt;Name&lt;\/label&gt;\n        &lt;InputText id=\"name\" @bind-Value=\"NewContact.Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div&gt;\n        &lt;label for=\"email\"&gt;Email&lt;\/label&gt;\n        &lt;InputText id=\"email\" @bind-Value=\"NewContact.Email\" \/&gt;\n    &lt;\/div&gt;\n    &lt;div&gt;\n        &lt;InputCheckbox id=\"send-me-deals\" @bind-Value=\"NewContact.SendMeDeals\" \/&gt;\n        &lt;label for=\"send-me-deals\"&gt;Send me deals&lt;\/label&gt;\n    &lt;\/div&gt;\n    &lt;button&gt;Submit&lt;\/button&gt;\n    &lt;AntiforgeryToken \/&gt;\n&lt;\/form&gt;\n\n@code {\n    [SupplyParameterFromForm]\n    public Contact NewContact { get; set; } = new();\n\n    public class Contact\n    {\n        public string Name { get; set; }\n        public string Email { get; set; }\n        public bool SendMeDeals { get; set; }\n    }\n\n    private async Task AddContact()\n    {\n        \/\/ Add contact...\n        NewContact = new();\n    }\n}<\/code><\/pre>\n<p>All server-side rendered forms now require a name, which is used to map submitted requests to the appropriate form handler method and for model binding. To specify the name of a plain HTML form, use the new <code>@formname<\/code> attribute. When using <code>EditForm<\/code>, specify the name using the <code>FormName<\/code> parameter.<\/p>\n<p>By default, form names need to be unique, but you can define a scope for form names using the <code>FormMappingScope<\/code> component.<\/p>\n<pre><code class=\"language-razor\">&lt;FormMappingScope Name=\"parent-context\"&gt;\n    &lt;ComponentWithFormBoundParameter \/&gt;\n&lt;\/FormMappingScope&gt;<\/code><\/pre>\n<p>Inputs based on <code>InputBase&lt;TValue&gt;<\/code> will generate form value names that match the names Blazor uses for model binding. You can specify the name Blazor should use to bind form data to the model using the <code>Name<\/code> property on <code>[SupplyParameterFromForm]<\/code>. You can also specify the name of the form whose data should be bound using the <code>Handler<\/code> property (in earlier previews the <code>Name<\/code> property was used for this purpose).<\/p>\n<p>To break a form up into multiple child components, derive your child components from <code>Editor&lt;T&gt;<\/code>. This will ensure that your child components generate the correct form field names based on the model.<\/p>\n<p><strong>Index.razor<\/strong><\/p>\n<pre><code class=\"language-razor\">&lt;EditForm Model=\"Customer\" method=\"post\" OnSubmit=\"DisplayCustomer\" FormName=\"customer\"&gt;\n    &lt;div&gt;\n        &lt;label&gt;Name&lt;\/label&gt;\n        &lt;InputText @bind-Value=\"Customer.Name\" \/&gt;\n    &lt;\/div&gt;\n    &lt;AddressEditor @bind-Value=\"Customer.BillingAddress\" \/&gt;\n    &lt;button&gt;Send&lt;\/button&gt;\n&lt;\/EditForm&gt;\n\n@if (submitted)\n{\n    &lt;!-- Display customer data --&gt;\n    &lt;h3&gt;Customer&lt;\/h3&gt;\n    &lt;p&gt;Name: @Customer.Name&lt;\/p&gt;\n    &lt;p&gt;Street: @Customer.BillingAddress.Street&lt;\/p&gt;\n    &lt;p&gt;City: @Customer.BillingAddress.City&lt;\/p&gt;\n    &lt;p&gt;State: @Customer.BillingAddress.State&lt;\/p&gt;\n    &lt;p&gt;Zip: @Customer.BillingAddress.Zip&lt;\/p&gt;\n}\n\n@code {\n    public void DisplayCustomer()\n    {\n        submitted = true;\n    }\n\n    [SupplyParameterFromForm] Customer? Customer { get; set; }\n\n    protected override void OnInitialized() =&gt; Customer ??= new();\n\n    bool submitted = false;\n    public void Submit() =&gt; submitted = true;\n}<\/code><\/pre>\n<p><strong>AddressEditor.razor<\/strong><\/p>\n<pre><code class=\"language-razor\">@inherits Editor&lt;Address&gt;\n\n&lt;div&gt;\n    &lt;label for=\"street\"&gt;Street&lt;\/label&gt;\n    &lt;InputText id=\"street\" @bind-Value=\"Value.Street\" \/&gt;\n&lt;\/div&gt;\n&lt;div&gt;\n    &lt;label for=\"state\"&gt;State&lt;\/label&gt;\n    &lt;InputText id=\"state\" @bind-Value=\"Value.State\" \/&gt;\n&lt;\/div&gt;\n&lt;div&gt;\n    &lt;label id=\"city\"&gt;City&lt;\/label&gt;\n    &lt;InputText for=\"city\" @bind-Value=\"Value.City\" \/&gt;\n&lt;\/div&gt;\n&lt;div&gt;\n    &lt;label for=\"zip\"&gt;Zip&lt;\/label&gt;\n    &lt;InputText id=\"zip\" @bind-Value=\"Value.Zip\" \/&gt;\n&lt;\/div&gt;<\/code><\/pre>\n<p>Model binding in Blazor now supports binding to the following additional types:<\/p>\n<ul>\n<li>Recursive types<\/li>\n<li>Types with constructors<\/li>\n<li>Enums<\/li>\n<\/ul>\n<p>You can also now use the <code>[DataMember]<\/code> and <code>[IgnoreDataMember]<\/code> attributes to customize model binding for a type you are authoring. You can use these attributes to rename properties, ignore properties, and mark properties as required. Additional model binding options are available from <code>RazorComponentOptions<\/code> when calling <code>AddRazorComponents<\/code>.<\/p>\n<h3>Auto render mode<\/h3>\n<p>The new <code>Auto<\/code> interactive render mode for Blazor web apps combines the strengths of the <code>Server<\/code> and <code>WebAssembly<\/code> render modes into a single dynamic option. The <code>Auto<\/code> render mode uses WebAssembly-based rendering if the .NET WebAssembly runtime can be loaded quickly (within 100ms). This typically is the case when the runtime was previously downloaded and cached, or when using a high speed network. Otherwise, the <code>Auto<\/code> render mode falls back to using the <code>Server<\/code> render mode while the .NET WebAssembly runtime is downloaded in the background.<\/p>\n<p>To use the <code>Auto<\/code> render mode, specify the <code>@rendermode=\"@RenderMode.Auto\"<\/code> attribute on the component instance, or <code>@attribute [RenderModeAuto]<\/code> on the component definition. Note that the component will need to be setup to run from both the server and the browser, so it will need to live in your client project and its implementation must not be tied to either <code>Server<\/code> or <code>WebAssembly<\/code>. Check out the <a href=\"https:\/\/github.com\/danroth27\/Net8BlazorAuto\">Blazor Auto render mode<\/a> sample to see how to set this up correctly.<\/p>\n<h3>Register root-level cascading values<\/h3>\n<p>Cascading values are a convenient way in Blazor to make state available to a subtree of the component hierarchy. You can now register root-level cascading values so they&#8217;re available for the entire component hierarchy.<\/p>\n<pre><code class=\"language-csharp\">\/\/ Registers a fixed cascading value\nservices.AddCascadingValue(sp =&gt; new MyCascadedThing { Value = 123 });\n\n\/\/ Registers a fixed cascading value by name\nservices.AddCascadingValue(\"thing\", sp =&gt; new MyCascadedThing { Value = 123 });\n\n\/\/ Registers a cascading value using CascadingValueSource&lt;TValue&gt;\nservices.AddCascadingValue(sp =&gt;\n{\n    var thing = new MyCascadedThing { Value = 456 };\n    var source = new CascadingValueSource&lt;MyCascadedThing&gt;(thing, isFixed: false);\n    return source;\n});<\/code><\/pre>\n<h3>Improved integration of interactive components with server-side rendering<\/h3>\n<p>Blazor in .NET 8 has advanced server-side rendering capabilities, like enhanced navigation and form handling. This preview improves the integration of interactive components with server-side rendering. Enhanced navigation, enhanced form handling, and streaming rendering can now add and remove interactive components and set parameters on them.<\/p>\n<h3>New <code>EmptyContent<\/code> parameter for <code>Virtualize<\/code><\/h3>\n<p>The <code>Virtualize<\/code> component now has an <code>EmptyContent<\/code> property that you can use to define what content should be shown when there are no items or when the <code>ItemsProvider<\/code> returns a result with a <code>TotalItemCount<\/code> equal to zero.<\/p>\n<p>Thank you <a href=\"https:\/\/github.com\/etemi\">@etemi<\/a> for this contribution!<\/p>\n<p><a id=\"identity\"><\/a><\/p>\n<h2>Identity<\/h2>\n<p>Previously in .NET 8 Preview 4 we added new <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/asp-net-core-updates-in-dotnet-8-preview-4\/#auth\">Identity API endpoints to register and login users<\/a> to simplify self-hosted identity management and make it easier to implement and customize identity in Single Page Apps (SPA) and Blazor apps. The default experience is cookie-based, so it &#8220;just works&#8221; for single domain apps. For scenarios that require tokens, such as accessing your web app from a mobile client, we now provide support for tokens &#8220;in the box.&#8221; These tokens are self-contained and use the same techniques for generation as cookie authentication. It is important to note <strong>these are not JWTs<\/strong> but self-contained and optimized for first-party apps with no delegated authentication. In multiple-server environments, you will need to configure <a href=\"https:\/\/learn.microsoft.com\/aspnet\/core\/security\/data-protection\/configuration\/overview\">data protection<\/a> to use shared storage.<\/p>\n<h3>Bearer token authentication handler<\/h3>\n<p>The new bearer token authentication handler integrates seamlessly with ASP.NET Core&#8217;s built-in authentication system. It can be used standalone (without relying on ASP.NET Core identity). It supports issuing and validating tokens. As of Preview 6, it also supports refresh tokens. ASP.NET Core Identity uses the <code>AddBearerToken<\/code> extension to integrate the handler with identity.<\/p>\n<h3>Identity API endpoints<\/h3>\n<p>The new .NET 8 set of identity API endpoints provide the HTTP-based APIs to:<\/p>\n<ul>\n<li><strong>Register<\/strong> a new user in the identity system<\/li>\n<li><strong>Login<\/strong> and exchange validated credentials for a cookie or token<\/li>\n<li><strong>Refresh<\/strong> credentials using a refresh token to keep the user logged in without having to re-enter their credentials<\/li>\n<li><strong>Confirm email<\/strong> for extra validation during the registration process<\/li>\n<li><strong>Resend confirmation email<\/strong> if it wasn&#8217;t received or expired<\/li>\n<li><strong>Reset password<\/strong> to support &#8220;forgot password&#8221; functionality<\/li>\n<\/ul>\n<p>There are also protected endpoints that require the user is authenticated to: <\/p>\n<ul>\n<li>Manage <strong>two-factor authentication (2FA)<\/strong>.<\/li>\n<li>Retrieve or update information in the <strong>user&#8217;s identity profile<\/strong>, including claims.<\/li>\n<\/ul>\n<h3>Get started<\/h3>\n<p>The <a href=\"https:\/\/github.com\/davidfowl\/IdentityEndpointsSample\">identity endpoints sample<\/a> by David Fowler shows how to configure identity to use the new handler and endpoints. It also contains an <a href=\"https:\/\/learn.microsoft.com\/aspnet\/core\/test\/http-files?view=aspnetcore-8.0\">.http file<\/a> to test the new endpoints inside of Visual Studio.<\/p>\n<p>First, enable the new handler and add authentication and authorization to the app.<\/p>\n<pre><code class=\"language-csharp\">builder.Services.AddAuthentication().AddBearerToken(IdentityConstants.BearerScheme);\nbuilder.Services.AddAuthorizationBuilder();<\/code><\/pre>\n<p>Next:<\/p>\n<ul>\n<li>Map the identity model<\/li>\n<li>Specify a <a href=\"https:\/\/learn.microsoft.com\/ef\/core\/dbcontext-configuration\/\">DbContext<\/a> for the identity store<\/li>\n<li>Opt-in to use the new endpoints<\/li>\n<\/ul>\n<pre><code class=\"language-csharp\">builder.Services.AddIdentityCore&lt;MyUser&gt;()\n   .AddEntityFrameworkStores&lt;AppDbContext&gt;()\n   .AddApiEndpoints();<\/code><\/pre>\n<p>After calling <code>Build()<\/code>, map the identity endpoints to routes in the application.<\/p>\n<pre><code class=\"language-csharp\">app.MapIdentityApi&lt;MyUser&gt;();<\/code><\/pre>\n<p>Now you can configure your APIs to use identity. This endpoint accesses identity to return the signed-in user&#8217;s name and is only available to authenticated users.<\/p>\n<pre><code class=\"language-csharp\">app.MapGet(\"\/\", (ClaimsPrincipal user) =&gt; $\"Hello {user.Identity!.Name}\").RequireAuthorization();<\/code><\/pre>\n<p>This is what an example session might look like:<\/p>\n<ol>\n<li>\n<p><strong>POST<\/strong> to the <code>\/register<\/code> endpoint to register the user.<\/p>\n<pre><code class=\"language-json\">{\n    \"user\" : \"test\",\n    \"password\" : \"@T35t!\",\n    \"email\" : \"test@notadomain.xyz\"\n}<\/code><\/pre>\n<\/li>\n<li>\n<p><strong>POST<\/strong> to the <code>\/login?cookieMode=false&amp;persistCookies=false<\/code> endpoint to login the user.<\/p>\n<pre><code class=\"language-json\">{\n    \"user\" : \"test\",\n    \"password\" : \"@T35t!\"\n}<\/code><\/pre>\n<\/li>\n<li>\n<p>Receive the <code>access_token<\/code>, expiration, and <code>refresh_token<\/code>.<\/p>\n<pre><code class=\"language-json\">{\n    \"token_type\": \"Bearer\",\n    \"access_token\": \"CfDJ9NHobblyWobblyGobblyGoop...\",\n    \"expires_in\": 3600,\n    \"refresh_token\": \"TokenBabbelYabbaDabbaDoo...\"\n}<\/code><\/pre>\n<\/li>\n<li>\n<p>Call a protected API using the token by setting the <code>Authorization<\/code> header of the request to <code>Bearer xxx<\/code> where <code>xxx<\/code> is the <code>access_token<\/code>.<\/p>\n<\/li>\n<li>\n<p>When the user&#8217;s credentials expire or are about to expire, <strong>POST<\/strong> to the <code>\/refresh<\/code> endpoint and pass the <code>refresh_token<\/code>.<\/p>\n<pre><code class=\"language-json\">{\n    \"refreshToken\": \"TokenBabbelYabbaDabbaDoo...\"\n}<\/code><\/pre>\n<\/li>\n<li>\n<p>This will generate a new <code>access_token<\/code>, expiration, and <code>refresh_token<\/code>.<\/p>\n<\/li>\n<\/ol>\n<p>These new building blocks make it easier to build authenticated apps that don&#8217;t delegate authentication.<\/p>\n<p><a id=\"spa\"><\/a><\/p>\n<h2>Single page apps<\/h2>\n<h3>New Visual Studio templates<\/h3>\n<p>We&#8217;ve been working closely with the Visual Studio team to ensure the <a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/exploring-javascript-and-typescript-development-in-visual-studio\/\">Visual Studio JavaScript &amp; TypeScript development<\/a> experience works great for ASP.NET Core developers. Visual Studio includes new project templates for Angular, React, and Vue that are built on the new JavaScript project system (.esproj) and integrate with ASP.NET Core backend projects.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2023\/09\/visual-studio-javascript-templates.png\" alt=\"Visual Studio JavaScript templates\" \/><\/p>\n<p>These Visual Studio templates come loaded with functionality for both .NET &amp; JavaScript developers:<\/p>\n<ul>\n<li>Get started fast with a JavaScript frontend and an ASP.NET Core backend.<\/li>\n<li>Stay up-to-date with the latest frontend framework versions.<\/li>\n<li>Integrate with the latest frontend framework command-line tooling.<\/li>\n<li>Templates for both JavaScript &amp; TypeScript.<\/li>\n<li>Rich JavaScript &amp; TypeScript code editing experience.<\/li>\n<li>Clean project separation for the frontend and backend.<\/li>\n<li>Integrate JavaScript build tools with your .NET build.<\/li>\n<li>npm dependency management UI.<\/li>\n<li>Compatible with Visual Studio Code debugging and launch configuration.<\/li>\n<li>Run frontend unit tests in test explorer using your favorite JavaScript test frameworks.<\/li>\n<\/ul>\n<p>To focus our efforts on providing the best possible development experience for using frontend JavaScript frameworks with ASP.NET Core, we&#8217;ve removed the existing Angular and React template from the .NET 8 SDK in favor of the new Visual Studio templates. We&#8217;re working with the Visual Studio team to further improve the new Visual Studio JavaScript with ASP.NET Core templates to support cross-platform development, integration with ASP.NET Core client web assets, simplified publishing, and targeting all supported .NET versions.<\/p>\n<p>You can give the new Visual Studio JavaScript templates a try by installing the latest <a href=\"https:\/\/visualstudio.com\/Preview\">Visual Studio preview<\/a> and then following one of the tutorials for <a href=\"https:\/\/learn.microsoft.com\/visualstudio\/javascript\/tutorial-asp-net-core-with-angular\">Angular<\/a>, <a href=\"https:\/\/learn.microsoft.com\/visualstudio\/javascript\/tutorial-asp-net-core-with-react\">React<\/a>, and <a href=\"https:\/\/learn.microsoft.com\/visualstudio\/javascript\/tutorial-asp-net-core-with-vue\">Vue<\/a> in the Visual Studio docs. If you have feedback on the new templates you can share it with us using the Visual Studio <a href=\"https:\/\/learn.microsoft.com\/visualstudio\/ide\/how-to-report-a-problem-with-visual-studio\">Send Feedback<\/a> tool.<\/p>\n<h2>Give feedback<\/h2>\n<p>We hope you enjoy this preview release of ASP.NET Core in .NET 8. Let us know what you think about these new improvements by filing issues on <a href=\"https:\/\/github.com\/dotnet\/aspnetcore\/issues\/new\">GitHub<\/a>.<\/p>\n<p>Thanks for trying out ASP.NET Core!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>.NET 8 Preview 7 is now available! Check out what&#8217;s new in ASP.NET Core in this update.<\/p>\n","protected":false},"author":417,"featured_media":47738,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,197,7509,7251],"tags":[7701],"class_list":["post-46797","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-aspnet","category-aspnetcore","category-blazor","tag-dotnet-8"],"acf":[],"blog_post_summary":"<p>.NET 8 Preview 7 is now available! Check out what&#8217;s new in ASP.NET Core in this update.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/46797","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/417"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=46797"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/46797\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/47738"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=46797"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=46797"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=46797"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}