diff --git a/aspnetcore/blazor/call-web-api.md b/aspnetcore/blazor/call-web-api.md index 51cccf452789..55770deffcd4 100644 --- a/aspnetcore/blazor/call-web-api.md +++ b/aspnetcore/blazor/call-web-api.md @@ -353,6 +353,37 @@ You can address this by flowing prerendered state using the Persistent Component :::moniker-end +:::moniker range=">= aspnetcore-9.0" + +## Client-side request streaming + +For Chromium-based browsers (for example, Google Chrome and Microsoft Edge) using the HTTP/2 protocol, and HTTPS, client-side Blazor uses [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit [request streaming](https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests). + +To enable request streaming, set to `true` on the . + +In the following file upload example: + +* `content` is the file's . +* `/Filesave` is the web API endpoint. +* `Http` is the . + +```csharp +var request = new HttpRequestMessage(HttpMethod.Post, "/Filesave"); +request.SetBrowserRequestStreamingEnabled(true); +request.Content = content; + +var response = await Http.SendAsync(request); +``` + +Streaming requests: + +* Require HTTPS protocol and don't work on HTTP/1.x. +* Include a body but not a `Content-Length` header. [CORS](xref:security/cors) with a preflight request is required for cross-origin streaming requests. + +For more information on file uploads with an component, see and the example at [Upload files to a server with client-side rendering (CSR)](xref:blazor/file-uploads#upload-files-to-a-server-with-client-side-rendering-csr). + +:::moniker-end + ## Add the `HttpClient` service *The guidance in this section applies to client-side scenarios.* @@ -368,10 +399,7 @@ In the `Program` file, add an service if it is ```csharp builder.Services.AddScoped(sp => - new HttpClient - { - BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) - }); + new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); ``` The preceding example sets the base address with `builder.HostEnvironment.BaseAddress` (), which gets the base address for the app and is typically derived from the `` tag's `href` value in the host page. @@ -385,10 +413,7 @@ If you're calling an external web API (not in the same URL space as the client a ```csharp builder.Services.AddScoped(sp => - new HttpClient - { - BaseAddress = new Uri("https://localhost:5001") - }); + new HttpClient { BaseAddress = new Uri("https://localhost:5001") }); ``` ## JSON helpers diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index 289ca58251f5..b0460b14dab6 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -111,26 +111,50 @@ If you're using the [Autofac Inversion of Control (IoC) container](https://autof ## File size read and upload limits -:::moniker range=">= aspnetcore-6.0" +:::moniker range=">= aspnetcore-9.0" + +For Chromium-based browsers (for example, Google Chrome and Microsoft Edge) using the HTTP/2 protocol, HTTPS, and [CORS](xref:security/cors), client-side Blazor supports using the [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit uploading large files with [request streaming](xref:blazor/call-web-api#client-side-request-streaming). -Server-side or client-side, there's no file read or upload size limit specifically for the component. However, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshalling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads (> 250 MB) may fail for client-side uploads using the component. For more information, see the following discussions: +Without a Chromium browser, HTTP/2 protocol, or HTTPS, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshaling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads may fail for client-side uploads using the component. :::moniker-end -:::moniker range="< aspnetcore-6.0" +:::moniker range="< aspnetcore-9.0" -The maximum supported file size for the component is 2 GB. Additionally, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshalling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads (> 250 MB) may fail for client-side uploads using the component. For more information, see the following discussions: +Client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshaling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads may fail for client-side uploads using the component. We recommend adopting [request streaming](xref:blazor/call-web-api?view=aspnetcore-9.0&preserve-view=true#client-side-request-streaming) with ASP.NET Core 9.0 or later. :::moniker-end -* [The Blazor InputFile Component should handle chunking when the file is uploaded (dotnet/runtime #84685)](https://github.com/dotnet/runtime/issues/84685) -* [Request Streaming upload via http handler (dotnet/runtime #36634)](https://github.com/dotnet/runtime/issues/36634) +## Security considerations + +### Avoid `IBrowserFile.Size` for file size limits + +Avoid using to impose a limit on the file size. Instead of using the unsafe client-supplied file size, explicitly specify the maximum file size. The following example uses the maximum file size assigned to `maxFileSize`: + +```diff +- var fileContent = new StreamContent(file.OpenReadStream(file.Size)); ++ var fileContent = new StreamContent(file.OpenReadStream(maxFileSize)); +``` + +### File name security + +Never use a client-supplied file name for saving a file to physical storage. Create a safe file name for the file using or to create a full path (including the file name) for temporary storage. -For large client-side file uploads that fail when attempting to use the component, we recommend chunking large files with a custom component using multiple [HTTP range requests](https://developer.mozilla.org/docs/Web/HTTP/Range_requests) instead of using the component. +Razor automatically HTML encodes property values for display. The following code is safe to use: - +```cshtml +@foreach (var file in Model.DatabaseFiles) { + + + @file.UntrustedName + + +} +``` -Work is currently scheduled for .NET 9 (late 2024) to address the client-side file size upload limitation. +Outside of Razor, always use to safely encode file names from a user's request. + +Many implementations must include a check that the file exists; otherwise, the file is overwritten by a file of the same name. Supply additional logic to meet your app's specifications. ## Examples @@ -269,7 +293,10 @@ public class UploadResult A security best practice for production apps is to avoid sending error messages to clients that might reveal sensitive information about an app, server, or network. Providing detailed error messages can aid a malicious user in devising attacks on an app, server, or network. The example code in this section only sends back an error code number (`int`) for display by the component client-side if a server-side error occurs. If a user requires assistance with a file upload, they provide the error code to support personnel for support ticket resolution without ever knowing the exact cause of the error. - + The following `LazyBrowserFileStream` class defines a custom stream type that lazily calls just before the first bytes of the stream are requested. The stream isn't transmitted from the browser to the server until reading the stream begins in .NET. @@ -277,7 +304,7 @@ The following `LazyBrowserFileStream` class defines a custom stream type that la - + :::moniker range=">= aspnetcore-8.0" @@ -342,11 +369,11 @@ The following `FileUpload2` component: :::moniker-end - + :::moniker range=">= aspnetcore-8.0" -If the component limits file uploads to a single file at a time or if the component only adopts interactive client-side rendering (CSR, `InteractiveWebAssembly`), the component can avoid the use of the `LazyBrowserFileStream` and use a . The following demonstrates the changes for the `FileUpload2` component: +If the component limits file uploads to a single file at a time or if the component only adopts client-side rendering (CSR, `InteractiveWebAssembly`), the component can avoid the use of the `LazyBrowserFileStream` and use a . The following demonstrates the changes for the `FileUpload2` component: ```diff - var stream = new LazyBrowserFileStream(file, maxFileSize); @@ -490,6 +517,12 @@ The server app must register controller services and map controller endpoints. F The following example demonstrates uploading files to a backend web API controller in a separate app, possibly on a separate server, from a component in a Blazor Web App that adopts CSR or a component in a Blazor WebAssembly app. +:::moniker range=">= aspnetcore-9.0" + +The example adopts [request streaming](xref:blazor/call-web-api#client-side-request-streaming) for a Chromium-based browser (for example, Google Chrome or Microsoft Edge) with HTTP/2 protocol and HTTPS. If request streaming can't be used, Blazor gracefully degrades to [Fetch API](https://developer.mozilla.org/docs/Web/API/Fetch_API) without request streaming. For more information, see the [File size read and upload limits](#file-size-read-and-upload-limits) section. + +:::moniker-end + The following `UploadResult` class maintains the result of an uploaded file. When a file fails to upload on the server, an error code is returned in `ErrorCode` for display to the user. A safe file name is generated on the server for each file and returned to the client in `StoredFileName` for display. Files are keyed between the client and server using the unsafe/untrusted file name in `FileName`. `UploadResult.cs`: @@ -528,15 +561,15 @@ A security best practice for production apps is to avoid sending error messages :::moniker range=">= aspnetcore-8.0" -In the Blazor Web App main project, add and related services in the project's `Program` file: +In the Blazor Web App server project, add and related services in the project's `Program` file: ```csharp builder.Services.AddHttpClient(); ``` -The `HttpClient` services must be added to the main project because the client-side component is prerendered on the server. If you [disable prerendering for the following component](xref:blazor/components/render-modes#prerendering), you aren't required to provide the `HttpClient` services in the main app and don't need to add the preceding line to the main project. +The services must be added to the server project because the client-side component is prerendered on the server. If you [disable prerendering for the following component](xref:blazor/components/render-modes#prerendering), you aren't required to provide the services in the server project and don't need to add the preceding line to the server project. -For more information on adding `HttpClient` services to an ASP.NET Core app, see . +For more information on adding services to an ASP.NET Core app, see . The client project (`.Client`) of a Blazor Web App must also register an for HTTP POST requests to a backend web API controller. Confirm or add the following to the client project's `Program` file: @@ -547,17 +580,177 @@ builder.Services.AddScoped(sp => The preceding example sets the base address with `builder.HostEnvironment.BaseAddress` (), which gets the base address for the app and is typically derived from the `` tag's `href` value in the host page. If you're calling an external web API, set the URI to the web API's base address. -Specify the Interactive WebAssembly render mode attribute at the top of the following component in a Blazor Web App: +A standalone Blazor WebAssembly app that uploads files to a separate server web API either uses a [named `HttpClient`](xref:blazor/call-web-api#named-httpclient-with-ihttpclientfactory) or sets the default service registration to point to the web API's endpoint. In the following example where the web API is hosted locally at port 5001, the base address is `https://localhost:5001`: + +```csharp +builder.Services.AddScoped(sp => + new HttpClient { BaseAddress = new Uri("https://localhost:5001") }); +``` + +:::moniker-end + +:::moniker range=">= aspnetcore-9.0" + +In a Blazor Web App, add the namespace to the component's directives: ```razor -@rendermode InteractiveWebAssembly +@using Microsoft.AspNetCore.Components.WebAssembly.Http ``` :::moniker-end `FileUpload2.razor`: -:::moniker range=">= aspnetcore-8.0" +:::moniker range=">= aspnetcore-9.0" + +```razor +@page "/file-upload-2" +@using System.Linq +@using System.Net.Http.Headers +@using System.Net +@inject HttpClient Http +@inject ILogger Logger + +File Upload 2 + +

File Upload Example 2

+ +

+ +

+ +@if (files.Count > 0) +{ +
+
+
    + @foreach (var file in files) + { +
  • + File: @file.Name +
    + @if (FileUpload(uploadResults, file.Name, Logger, + out var result)) + { + + Stored File Name: @result.StoredFileName + + } + else + { + + There was an error uploading the file + (Error: @result.ErrorCode). + + } +
  • + } +
+
+
+} + +@code { + private List files = new(); + private List uploadResults = new(); + private int maxAllowedFiles = 3; + private bool shouldRender; + + protected override bool ShouldRender() => shouldRender; + + private async Task OnInputFileChange(InputFileChangeEventArgs e) + { + shouldRender = false; + long maxFileSize = 1024 * 15; + var upload = false; + + using var content = new MultipartFormDataContent(); + + foreach (var file in e.GetMultipleFiles(maxAllowedFiles)) + { + if (uploadResults.SingleOrDefault( + f => f.FileName == file.Name) is null) + { + try + { + files.Add(new() { Name = file.Name }); + + var fileContent = new StreamContent(file.OpenReadStream(maxFileSize)); + + fileContent.Headers.ContentType = + new MediaTypeHeaderValue(file.ContentType); + + content.Add( + content: fileContent, + name: "\"files\"", + fileName: file.Name); + + upload = true; + } + catch (Exception ex) + { + Logger.LogInformation( + "{FileName} not uploaded (Err: 6): {Message}", + file.Name, ex.Message); + + uploadResults.Add( + new() + { + FileName = file.Name, + ErrorCode = 6, + Uploaded = false + }); + } + } + } + + if (upload) + { + var request = new HttpRequestMessage(HttpMethod.Post, "/Filesave"); + request.SetBrowserRequestStreamingEnabled(true); + request.Content = content; + + var response = await Http.SendAsync(request); + + var newUploadResults = await response.Content + .ReadFromJsonAsync>(); + + if (newUploadResults is not null) + { + uploadResults = uploadResults.Concat(newUploadResults).ToList(); + } + } + + shouldRender = true; + } + + private static bool FileUpload(IList uploadResults, + string? fileName, ILogger logger, out UploadResult result) + { + result = uploadResults.SingleOrDefault(f => f.FileName == fileName) ?? new(); + + if (!result.Uploaded) + { + logger.LogInformation("{FileName} not uploaded (Err: 5)", fileName); + result.ErrorCode = 5; + } + + return result.Uploaded; + } + + private class File + { + public string? Name { get; set; } + } +} +``` + +:::moniker-end + +:::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_WebAssembly/Pages/FileUpload2.razor"::: @@ -597,7 +790,13 @@ Because the example uses the app's [environment](xref:blazor/fundamentals/enviro > [!WARNING] > The example saves files without scanning their contents, and the guidance in this article doesn't take into account additional security best practices for uploaded files. On staging and production systems, disable execute permission on the upload folder and scan files with an anti-virus/anti-malware scanner API immediately after upload. For more information, see . -In the following example, update the shared project's namespace to match the shared project if a shared project is supplying the `UploadResult` class. +In the following example for a hosted Blazor WebAssembly app or where a shared project is used to supply the `UploadResult` class, add the shared project's namespace: + +```csharp +using BlazorSample.Shared; +``` + +We recommend using a namespace for the following controller (for example: `namespace BlazorSample.Controllers`). `Controllers/FilesaveController.cs`: @@ -610,7 +809,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -using BlazorSample.Shared; [ApiController] [Route("[controller]")] @@ -697,15 +895,72 @@ public class FilesaveController( In the preceding code, is called to generate a secure file name. Never trust the file name provided by the browser, as a cyberattacker may choose an existing file name that overwrites an existing file or send a path that attempts to write outside of the app. -The server app must register controller services and map controller endpoints. For more information, see . +The server app must register controller services and map controller endpoints. For more information, see . We recommend adding controller services with in order to automatically [mitigate Cross-Site Request Forgery (XSRF/CSRF) attacks](xref:security/anti-request-forgery) for authenticated users. If you merely use , antiforgery isn't enabled automatically. For more information, see . + +:::moniker range=">= aspnetcore-9.0" + +Cross-Origin Requests (CORS) configuration on the server is required for [request streaming](https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests) when the server is hosted at a different origin, and a preflight request is always made by the client. In the service configuration of the server's `Program` file (the server project of a Blazor Web App or the backend server web API of a Blazor WebAssembly app), the following default CORS policy is suitable for testing with the examples in this article. The client makes the local request from port 5003. Change the port number to match the client app port that you're using: + +:::moniker-end + +:::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" + +Configure Cross-Origin Requests (CORS) on the server. In the service configuration of the server's `Program` file (the server project of a Blazor Web App or the backend server web API of a Blazor WebAssembly app), the following default CORS policy is suitable for testing with the examples in this article. The client makes the local request from port 5003. Change the port number to match the client app port that you're using: + +:::moniker-end + +:::moniker range="< aspnetcore-8.0" + +Configure Cross-Origin Requests (CORS) on the server. In the service configuration of the backend server web API's `Program` file, the following default CORS policy is suitable for testing with the examples in this article. The client makes the local request from port 5003. Change the port number to match the client app port that you're using: - +For more information, see . + +:::moniker range=">= aspnetcore-9.0" + +Configure the server's maximum request body size and multipart body length limits if the limits constrain the upload size. + +For the Kestrel server, set (default: 30,000,000 bytes) and (default: 134,217,728 bytes). Set the `maxFileSize` variable in the component and the controller to the same value. + +In the following `Program` file Kestrel configuration (the server project of a Blazor Web App or the backend server web API of a Blazor WebAssembly app), the `{LIMIT}` placeholder is the limit in bytes: + +```csharp +using Microsoft.AspNetCore.Http.Features; + +... + +builder.WebHost.ConfigureKestrel(serverOptions => +{ + serverOptions.Limits.MaxRequestBodySize = {LIMIT}; +}); + +builder.Services.Configure(options => +{ + options.MultipartBodyLengthLimit = {LIMIT}; +}); +``` + +:::moniker-end ## Cancel a file upload @@ -1038,13 +1293,13 @@ The line that calls to `true` in the [server-side circuit handler hub options](xref:blazor/fundamentals/signalr#server-side-circuit-handler-options). For more information, see [FileUpload: Did not receive any data in the allotted time (`dotnet/aspnetcore` #38842)](https://github.com/dotnet/aspnetcore/issues/38842#issuecomment-1342540950). +* Using the [Autofac Inversion of Control (IoC) container](https://autofac.org/) instead of the built-in ASP.NET Core dependency injection container in versions of ASP.NET Core earlier than 9.0. To resolve the issue, set to `true` in the [server-side circuit handler hub options](xref:blazor/fundamentals/signalr#server-side-circuit-handler-options). For more information, see [FileUpload: Did not receive any data in the allotted time (`dotnet/aspnetcore` #38842)](https://github.com/dotnet/aspnetcore/issues/38842#issuecomment-1342540950). * Not reading the stream to completion. This isn't a framework issue. Trap the exception and investigate it further in your local environment/network. - + * Using server-side rendering and calling on multiple files before reading them to completion. To resolve the issue, use the `LazyBrowserFileStream` class and approach described in the [Upload files to a server with server-side rendering](#upload-files-to-a-server-with-server-side-rendering) section of this article. diff --git a/aspnetcore/fundamentals/servers/kestrel/endpoints.md b/aspnetcore/fundamentals/servers/kestrel/endpoints.md index cd8814234a57..a92bd56852e9 100644 --- a/aspnetcore/fundamentals/servers/kestrel/endpoints.md +++ b/aspnetcore/fundamentals/servers/kestrel/endpoints.md @@ -581,6 +581,22 @@ The following example configures an endpoint for HTTP/1.1, HTTP/2, and HTTP/3 co :::code language="csharp" source="~/fundamentals/servers/kestrel/samples/6.x/KestrelSample/Snippets/Program.cs" id="snippet_ConfigureKestrelProtocols"::: +:::moniker-end + +:::moniker range=">= aspnetcore-9.0" + +## Customize Kestrel named pipe endpoints + +Kestrel's named pipe support includes advanced customization options. The [CreateNamedPipeServerStream](/dotnet/api/microsoft.aspnetcore.server.kestrel.transport.namedpipes.namedpipetransportoptions.createnamedpipeserverstream) property on the named pipe options allows pipes to be customized per-endpoint. + +This is useful, for example, in a Kestrel app that requires two pipe endpoints with different [access security](/windows/win32/ipc/named-pipe-security-and-access-rights). The `CreateNamedPipeServerStream` option can be used to create pipes with custom security settings, depending on the pipe name. + +:::code language="csharp" source="~/fundamentals/servers/kestrel/endpoints/samples/KestrelNamedEP/Program.cs" highlight="15-33" id="snippet_1"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-8.0" + ## See also * diff --git a/aspnetcore/fundamentals/servers/kestrel/endpoints/samples/KestrelNamedEP/Program.cs b/aspnetcore/fundamentals/servers/kestrel/endpoints/samples/KestrelNamedEP/Program.cs index dbd4d0547df4..7376d913fc57 100644 --- a/aspnetcore/fundamentals/servers/kestrel/endpoints/samples/KestrelNamedEP/Program.cs +++ b/aspnetcore/fundamentals/servers/kestrel/endpoints/samples/KestrelNamedEP/Program.cs @@ -1,21 +1,42 @@ +// using System.IO.Pipes; +using System.Security.AccessControl; +using System.Security.Principal; var builder = WebApplication.CreateBuilder(); builder.WebHost.ConfigureKestrel(options => { - options.ListenNamedPipe("pipe1"); - options.ListenNamedPipe("pipe2"); + options.ListenNamedPipe("defaultPipe"); + options.ListenNamedPipe("securedPipe"); }); builder.WebHost.UseNamedPipes(options => { options.CreateNamedPipeServerStream = (context) => { - var pipeSecurity = CreatePipeSecurity(context.NamedPipeEndpoint.PipeName); + var pipeName = context.NamedPipeEndPoint.PipeName; + + switch (pipeName) + { + case "defaultPipe": + return NamedPipeTransportOptions.CreateDefaultNamedPipeServerStream(context); + case "securedPipe": + var allowSecurity = new PipeSecurity(); + allowSecurity.AddAccessRule(new PipeAccessRule("Users", PipeAccessRights.FullControl, AccessControlType.Allow)); - return NamedPipeServerStreamAcl.Create(context.NamedPipeEndPoint.PipeName, PipeDirection.InOut, - NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, - context.PipeOptions, inBufferSize: 0, outBufferSize: 0, pipeSecurity); + return NamedPipeServerStreamAcl.Create(pipeName, PipeDirection.InOut, + NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, + context.PipeOptions, inBufferSize: 0, outBufferSize: 0, allowSecurity); + default: + throw new InvalidOperationException($"Unexpected pipe name: {pipeName}"); + } }; }); + +var app = builder.Build(); + +app.MapGet("/", () => "Hello World!"); + +app.Run(); +// diff --git a/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md index 8c5a71644ec0..dccb6c5da4a4 100644 --- a/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md @@ -227,3 +227,12 @@ Trigger JavaScript callbacks either before or after enhanced navigation with new * `blazor.addEventListener("enhancednavigationend", {CALLBACK})` For more information, see . + +### Client-side request streaming + +Interactive WebAssembly rendering in Blazor now supports client-side request streaming using the `request.SetBrowserReqeustStreamingEnabled(true)` option on `HttpRequestMessage`. + +For more information, see the following resources: + +* +* diff --git a/aspnetcore/security/authentication/social/microsoft-logins.md b/aspnetcore/security/authentication/social/microsoft-logins.md index d37f11f765a0..548604d0bb87 100644 --- a/aspnetcore/security/authentication/social/microsoft-logins.md +++ b/aspnetcore/security/authentication/social/microsoft-logins.md @@ -4,7 +4,7 @@ author: rick-anderson description: This sample demonstrates the integration of Microsoft account user authentication into an existing ASP.NET Core app. ms.author: riande ms.custom: mvc -ms.date: 12/08/2021 +ms.date: 03/01/2025 monikerRange: '>= aspnetcore-3.1' uid: security/authentication/microsoft-logins --- @@ -14,39 +14,23 @@ By [Valeriy Novytskyy](https://github.com/01binary) and [Rick Anderson](https:// :::moniker range=">= aspnetcore-6.0" -This sample shows you how to enable users to sign in with their work, school, or personal Microsoft account using the ASP.NET Core 6.0 project created on the [previous page](xref:security/authentication/social/index). +This sample shows how to enable users to sign in with their work, school, or personal Microsoft account using the ASP.NET Core project created on the [previous page](xref:security/authentication/social/index). -## Create the app in Microsoft Developer Portal +## Create the app in the Microsoft Entra admin center * Add the [Microsoft.AspNetCore.Authentication.MicrosoftAccount](https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.MicrosoftAccount/) NuGet package to the project. -* Navigate to the [Azure portal - App registrations](https://go.microsoft.com/fwlink/?linkid=2083908) page and create or sign into a Microsoft account: +* Register the application in the Microsoft Entra admin center by following the steps in [Register an application with the Microsoft identity platform](/entra/identity-platform/quickstart-register-app?tabs=client-secret) -If you don't have a Microsoft account, select **Create one**. After signing in, you are redirected to the **App registrations** page: +### Create a client secret -* Select **New registration** -* Enter a **Name**. -* Select an option for **Supported account types**. - * The `MicrosoftAccount` package supports App Registrations created using "Accounts in any organizational directory" or "Accounts in any organizational directory and Microsoft accounts" options by default. - * To use other options, set `AuthorizationEndpoint` and `TokenEndpoint` members of `MicrosoftAccountOptions` used to initialize the Microsoft Account authentication to the URLs displayed on **Endpoints** page of the App Registration after it is created (available by clicking Endpoints on the **Overview** page). -* Under **Redirect URI**, enter your development URL with `/signin-microsoft` appended. For example, `https://localhost:5001/signin-microsoft`. The Microsoft authentication scheme configured later in this sample will automatically handle requests at `/signin-microsoft` route to implement the OAuth flow. -* Select **Register** - -### Create client secret - -* In the left pane, select **Certificates & secrets**. -* Under **Client secrets**, select **New client secret** - * Add a description for the client secret. - * Select the **Add** button. -* Under **Client secrets**, copy the value of the client secret. - -The URI segment `/signin-microsoft` is set as the default callback of the Microsoft authentication provider. You can change the default callback URI while configuring the Microsoft authentication middleware via the inherited property of the class. +Generate a client secret in the Microsoft Entra admin center by following the steps in [Register an application with the Microsoft identity platform: Add Credentials](/entra/identity-platform/quickstart-register-app?tabs=client-secret#add-credentials). ## Store the Microsoft client ID and secret -Store sensitive settings such as the Microsoft **Application (client) ID** found on the **Overview** page of the App Registration and **Client Secret** you created on the **Certificates & secrets page** with [Secret Manager](xref:security/app-secrets). For this sample, use the following steps: +Store sensitive settings such as the Microsoft **Application (client) ID** and **Client Secret** created in the previous step with [Secret Manager](xref:security/app-secrets). For this sample, use the following steps: 1. Initialize the project for secret storage per the instructions at [Enable secret storage](xref:security/app-secrets#enable-secret-storage). -1. Store the sensitive settings in the local secret store with the secret keys `Authentication:Microsoft:ClientId` and `Authentication:Microsoft:ClientSecret`: +1. Store the sensitive settings in the local secret store with the secret keys `Authentication:Microsoft:ClientId` and `Authentication:Microsoft:ClientSecret`. The `` is listed on the Azure App registrations blade under **Application (client) ID**. The `` is on listed under **Certificates & secrets** as the **Value**, not the **Secret ID**. ```dotnetcli dotnet user-secrets set "Authentication:Microsoft:ClientId" "" @@ -59,7 +43,7 @@ Store sensitive settings such as the Microsoft **Application (client) ID** found Add the Authentication service to the `Program`: -[!code-csharp[](~/security/authentication/social/social-code/6.x/ProgramMS.cs)] +:::code language="csharp" source="~/security/authentication/social/social-code/6.x/ProgramMS.cs" id="snippet_AddServices"::: [!INCLUDE [default settings configuration](includes/default-settings.md)] @@ -68,10 +52,10 @@ For more information about configuration options supported by Microsoft Account ## Sign in with Microsoft Account * Run the app and select **Log in**. An option to sign in with Microsoft appears. -* Select to sign in with Microsoft. You are redirected to Microsoft for authentication. After signing in with your Microsoft Account, you will be prompted to let the app access your info: -* Select **Yes**. You are redirected back to the web site where you can set your email. +* Select to sign in with Microsoft to navigate to Microsoft for authentication. After signing in with your Microsoft Account, you'll be prompted to let the app access your info: +* Select **Yes** to navigate back to the web site where to set your email. -You are now logged in using your Microsoft credentials. +You're now logged in using your Microsoft credentials. [!INCLUDE[](includes/chain-auth-providers.md)] @@ -79,19 +63,19 @@ You are now logged in using your Microsoft credentials. ## Troubleshooting -* If the Microsoft Account provider redirects you to a sign in error page, note the error title and description query string parameters directly following the `#` (hashtag) in the Uri. +* If the Microsoft Account provider redirects to a sign in error page, note the error title and description query string parameters directly following the `#` (hashtag) in the Uri. Although the error message seems to indicate a problem with Microsoft authentication, the most common cause is your application Uri not matching any of the **Redirect URIs** specified for the **Web** platform. * If Identity isn't configured by calling `services.AddIdentity` in `ConfigureServices`, attempting to authenticate will result in *ArgumentException: The 'SignInScheme' option must be provided*. The project template used in this sample ensures that this is done. -* If the site database has not been created by applying the initial migration, you will get *A database operation failed while processing the request* error. Tap **Apply Migrations** to create the database and refresh to continue past the error. +* If the site database hasn't been created by applying the initial migration, *A database operation failed while processing the request* error occurs. Tap **Apply Migrations** to create the database and refresh to continue past the error. ## Next steps -* This article showed how you can authenticate with Microsoft. You can follow a similar approach to authenticate with other providers listed on the [previous page](xref:security/authentication/social/index). -* Once you publish your web site to Azure web app, create a new client secrets in the Microsoft Developer Portal. -* Set the `Authentication:Microsoft:ClientId` and `Authentication:Microsoft:ClientSecret` as application settings in the Azure portal. The configuration system is set up to read keys from environment variables. +* This article showed how to authenticate with Microsoft. Follow a similar approach to authenticate with other providers listed on the [previous page](xref:security/authentication/social/index). +* Once the web site is published to Azure web app, create a new client secrets in the Microsoft Entra admin center. +* Set the `Authentication:Microsoft:ClientId` and `Authentication:Microsoft:ClientSecret` as application settings in the Microsoft Entra admin center. The configuration system is set up to read keys from environment variables. :::moniker-end @@ -99,34 +83,18 @@ You are now logged in using your Microsoft credentials. This sample shows you how to enable users to sign in with their work, school, or personal Microsoft account using the ASP.NET Core 3.0 project created on the [previous page](xref:security/authentication/social/index). -## Create the app in Microsoft Developer Portal +## Create the app in the Microsoft Entra admin center * Add the [Microsoft.AspNetCore.Authentication.MicrosoftAccount](https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.MicrosoftAccount/) NuGet package to the project. -* Navigate to the [Azure portal - App registrations](https://go.microsoft.com/fwlink/?linkid=2083908) page and create or sign into a Microsoft account: - -If you don't have a Microsoft account, select **Create one**. After signing in, you are redirected to the **App registrations** page: - -* Select **New registration** -* Enter a **Name**. -* Select an option for **Supported account types**. - * The `MicrosoftAccount` package supports App Registrations created using "Accounts in any organizational directory" or "Accounts in any organizational directory and Microsoft accounts" options by default. - * To use other options, set `AuthorizationEndpoint` and `TokenEndpoint` members of `MicrosoftAccountOptions` used to initialize the Microsoft Account authentication to the URLs displayed on **Endpoints** page of the App Registration after it is created (available by clicking Endpoints on the **Overview** page). -* Under **Redirect URI**, enter your development URL with `/signin-microsoft` appended. For example, `https://localhost:5001/signin-microsoft`. The Microsoft authentication scheme configured later in this sample will automatically handle requests at `/signin-microsoft` route to implement the OAuth flow. -* Select **Register** +* Register the application in the Microsoft Entra admin center by following the steps in [Register an application with the Microsoft identity platform](/entra/identity-platform/quickstart-register-app?tabs=client-secret#register-an-application) ### Create client secret -* In the left pane, select **Certificates & secrets**. -* Under **Client secrets**, select **New client secret** - * Add a description for the client secret. - * Select the **Add** button. -* Under **Client secrets**, copy the value of the client secret. - -The URI segment `/signin-microsoft` is set as the default callback of the Microsoft authentication provider. You can change the default callback URI while configuring the Microsoft authentication middleware via the inherited property of the class. +Generate a client secret in the Microsoft Entra admin center by following the steps in [Register an application with the Microsoft identity platform: Add Credentials](/entra/identity-platform/quickstart-register-app?tabs=client-secret#add-credentials). ## Store the Microsoft client ID and secret -Store sensitive settings such as the Microsoft **Application (client) ID** found on the **Overview** page of the App Registration and **Client Secret** you created on the **Certificates & secrets page** with [Secret Manager](xref:security/app-secrets). For this sample, use the following steps: +Store sensitive settings such as the Microsoft **Application (client) ID** and **Client Secret** you created in the previous step with [Secret Manager](xref:security/app-secrets). For this sample, use the following steps: 1. Initialize the project for secret storage per the instructions at [Enable secret storage](xref:security/app-secrets#enable-secret-storage). 1. Store the sensitive settings in the local secret store with the secret keys `Authentication:Microsoft:ClientId` and `Authentication:Microsoft:ClientSecret`: @@ -150,11 +118,11 @@ For more information about configuration options supported by Microsoft Account ## Sign in with Microsoft Account -Run the app and select **Log in**. An option to sign in with Microsoft appears. When you select on Microsoft, you are redirected to Microsoft for authentication. After signing in with your Microsoft Account, you will be prompted to let the app access your info: +Run the app and select **Log in**. An option to sign in with Microsoft appears. Select **Microsoft** to navigate to Microsoft for authentication. After signing in with your Microsoft Account, you'll be prompted to let the app access your info: -Tap **Yes** and you will be redirected back to the web site where you can set your email. +Tap **Yes** and you'll be redirected back to the web site where you can set your email. -You are now logged in using your Microsoft credentials. +You're now logged in using your Microsoft credentials. [!INCLUDE[](includes/chain-auth-providers.md)] @@ -166,12 +134,12 @@ You are now logged in using your Microsoft credentials. Although the error message seems to indicate a problem with Microsoft authentication, the most common cause is your application Uri not matching any of the **Redirect URIs** specified for the **Web** platform. * If Identity isn't configured by calling `services.AddIdentity` in `ConfigureServices`, attempting to authenticate will result in *ArgumentException: The 'SignInScheme' option must be provided*. The project template used in this sample ensures that this is done. -* If the site database has not been created by applying the initial migration, you will get *A database operation failed while processing the request* error. Tap **Apply Migrations** to create the database and refresh to continue past the error. +* If the site database hasn't been created by applying the initial migration, you'll get *A database operation failed while processing the request* error. Tap **Apply Migrations** to create the database and refresh to continue past the error. ## Next steps * This article showed how you can authenticate with Microsoft. You can follow a similar approach to authenticate with other providers listed on the [previous page](xref:security/authentication/social/index). -* Once you publish your web site to Azure web app, create a new client secrets in the Microsoft Developer Portal. -* Set the `Authentication:Microsoft:ClientId` and `Authentication:Microsoft:ClientSecret` as application settings in the Azure portal. The configuration system is set up to read keys from environment variables. +* Once you publish your web site to Azure web app, create a new client secrets in the Microsoft Entra admin center. +* Set the `Authentication:Microsoft:ClientId` and `Authentication:Microsoft:ClientSecret` as application settings in Microsoft Entra admin center. The configuration system is set up to read keys from environment variables. :::moniker-end diff --git a/aspnetcore/security/authentication/social/social-code/6.x/ProgramMS.cs b/aspnetcore/security/authentication/social/social-code/6.x/ProgramMS.cs index 879c70b75092..7429286cf469 100644 --- a/aspnetcore/security/authentication/social/social-code/6.x/ProgramMS.cs +++ b/aspnetcore/security/authentication/social/social-code/6.x/ProgramMS.cs @@ -1,9 +1,8 @@ var builder = WebApplication.CreateBuilder(args); -var services = builder.Services; -var configuration = builder.Configuration; - -services.AddAuthentication().AddMicrosoftAccount(microsoftOptions => - { - microsoftOptions.ClientId = configuration["Authentication:Microsoft:ClientId"]; - microsoftOptions.ClientSecret = configuration["Authentication:Microsoft:ClientSecret"]; - }); +// +builder.Services.AddAuthentication().AddMicrosoftAccount(microsoftOptions => +{ + microsoftOptions.ClientId = configuration["Authentication:Microsoft:ClientId"]; + microsoftOptions.ClientSecret = configuration["Authentication:Microsoft:ClientSecret"]; +}); +//