diff --git a/aspnetcore/signalr/authn-and-authz.md b/aspnetcore/signalr/authn-and-authz.md index 321a5cc164db..faf98b4dfba1 100644 --- a/aspnetcore/signalr/authn-and-authz.md +++ b/aspnetcore/signalr/authn-and-authz.md @@ -84,8 +84,6 @@ Cookies are a browser-specific way to send access tokens, but non-browser client The client can provide an access token instead of using a cookie. The server validates the token and uses it to identify the user. This validation is done only when the connection is established. During the life of the connection, the server doesn't automatically revalidate to check for token revocation. -On the server, bearer token authentication is configured using the [JWT Bearer middleware](/dotnet/api/microsoft.extensions.dependencyinjection.jwtbearerextensions.addjwtbearer). - In the JavaScript client, the token can be provided using the [accessTokenFactory](xref:signalr/configuration#configure-bearer-authentication) option. [!code-typescript[Configure Access Token](authn-and-authz/sample/wwwroot/js/chat.ts?range=52-55)] @@ -104,7 +102,11 @@ var connection = new HubConnectionBuilder() > [!NOTE] > The access token function you provide is called before **every** HTTP request made by SignalR. If you need to renew the token in order to keep the connection active (because it may expire during the connection), do so from within this function and return the updated token. -In standard web APIs, bearer tokens are sent in an HTTP header. However, SignalR is unable to set these headers in browsers when using some transports. When using WebSockets and Server-Sent Events, the token is transmitted as a query string parameter. To support this on the server, additional configuration is required: +In standard web APIs, bearer tokens are sent in an HTTP header. However, SignalR is unable to set these headers in browsers when using some transports. When using WebSockets and Server-Sent Events, the token is transmitted as a query string parameter. + +#### Built-in JWT authentication + +On the server, bearer token authentication is configured using the [JWT Bearer middleware](xref:Microsoft.Extensions.DependencyInjection.JwtBearerExtensions.AddJwtBearer%2A): [!code-csharp[Configure Server to accept access token from Query String](authn-and-authz/sample/Startup.cs?name=snippet)] @@ -113,6 +115,48 @@ In standard web APIs, bearer tokens are sent in an HTTP header. However, SignalR > [!NOTE] > The query string is used on browsers when connecting with WebSockets and Server-Sent Events due to browser API limitations. When using HTTPS, query string values are secured by the TLS connection. However, many servers log query string values. For more information, see [Security considerations in ASP.NET Core SignalR](xref:signalr/security). SignalR uses headers to transmit tokens in environments which support them (such as the .NET and Java clients). +#### Identity Server JWT authentication + +When using Identity Server, add a service to the project: + +```csharp +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.Extensions.Options; +public class ConfigureJwtBearerOptions : IPostConfigureOptions +{ + public void PostConfigure(string name, JwtBearerOptions options) + { + var originalOnMessageReceived = options.Events.OnMessageReceived; + options.Events.OnMessageReceived = async context => + { + await originalOnMessageReceived(context); + + if (string.IsNullOrEmpty(context.Token)) + { + var accessToken = context.Request.Query["access_token"]; + var path = context.HttpContext.Request.Path; + + if (!string.IsNullOrEmpty(accessToken) && + path.StartsWithSegments("/hubs")) + { + context.Token = accessToken; + } + } + }; + } +} +``` + +Register the service in `Startup.ConfigureServices` after adding services for authentication () and the authentication handler for Identity Server (): + +```csharp +services.AddAuthentication() + .AddIdentityServerJwt(); +services.TryAddEnumerable( + ServiceDescriptor.Singleton, + ConfigureJwtBearerOptions>()); +``` + ### Cookies vs. bearer tokens Cookies are specific to browsers. Sending them from other kinds of clients adds complexity compared to sending bearer tokens. Consequently, cookie authentication isn't recommended unless the app only needs to authenticate users from the browser client. Bearer token authentication is the recommended approach when using clients other than the browser client. diff --git a/aspnetcore/tutorials/signalr-blazor-webassembly.md b/aspnetcore/tutorials/signalr-blazor-webassembly.md index a3d18dc6a9ec..22452a71912c 100644 --- a/aspnetcore/tutorials/signalr-blazor-webassembly.md +++ b/aspnetcore/tutorials/signalr-blazor-webassembly.md @@ -364,8 +364,9 @@ To learn more about building Blazor apps, see the Blazor documentation: > [!div class="nextstepaction"] > +> [Bearer token authentication with Identity Server, WebSockets, and Server-Sent Events](xref:signalr/authn-and-authz#bearer-token-authentication) ## Additional resources * -* [SignalR cross-origin negotiation for authentication](xref:blazor/fundamentals/additional-scenarios#signalr-cross-origin-negotiation-for-authentication) \ No newline at end of file +* [SignalR cross-origin negotiation for authentication](xref:blazor/fundamentals/additional-scenarios#signalr-cross-origin-negotiation-for-authentication)