Skip to content

Commit d94bb6d

Browse files
authored
IdS with WebSockets and Server-Sent Events (#20269)
1 parent cbc19f1 commit d94bb6d

File tree

2 files changed

+49
-4
lines changed

2 files changed

+49
-4
lines changed

aspnetcore/signalr/authn-and-authz.md

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,6 @@ Cookies are a browser-specific way to send access tokens, but non-browser client
8484

8585
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.
8686

87-
On the server, bearer token authentication is configured using the [JWT Bearer middleware](/dotnet/api/microsoft.extensions.dependencyinjection.jwtbearerextensions.addjwtbearer).
88-
8987
In the JavaScript client, the token can be provided using the [accessTokenFactory](xref:signalr/configuration#configure-bearer-authentication) option.
9088

9189
[!code-typescript[Configure Access Token](authn-and-authz/sample/wwwroot/js/chat.ts?range=52-55)]
@@ -104,7 +102,11 @@ var connection = new HubConnectionBuilder()
104102
> [!NOTE]
105103
> 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.
106104
107-
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:
105+
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.
106+
107+
#### Built-in JWT authentication
108+
109+
On the server, bearer token authentication is configured using the [JWT Bearer middleware](xref:Microsoft.Extensions.DependencyInjection.JwtBearerExtensions.AddJwtBearer%2A):
108110

109111
[!code-csharp[Configure Server to accept access token from Query String](authn-and-authz/sample/Startup.cs?name=snippet)]
110112

@@ -113,6 +115,48 @@ In standard web APIs, bearer tokens are sent in an HTTP header. However, SignalR
113115
> [!NOTE]
114116
> 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).
115117
118+
#### Identity Server JWT authentication
119+
120+
When using Identity Server, add a <xref:Microsoft.Extensions.Options.PostConfigureOptions%601> service to the project:
121+
122+
```csharp
123+
using Microsoft.AspNetCore.Authentication.JwtBearer;
124+
using Microsoft.Extensions.Options;
125+
public class ConfigureJwtBearerOptions : IPostConfigureOptions<JwtBearerOptions>
126+
{
127+
public void PostConfigure(string name, JwtBearerOptions options)
128+
{
129+
var originalOnMessageReceived = options.Events.OnMessageReceived;
130+
options.Events.OnMessageReceived = async context =>
131+
{
132+
await originalOnMessageReceived(context);
133+
134+
if (string.IsNullOrEmpty(context.Token))
135+
{
136+
var accessToken = context.Request.Query["access_token"];
137+
var path = context.HttpContext.Request.Path;
138+
139+
if (!string.IsNullOrEmpty(accessToken) &&
140+
path.StartsWithSegments("/hubs"))
141+
{
142+
context.Token = accessToken;
143+
}
144+
}
145+
};
146+
}
147+
}
148+
```
149+
150+
Register the service in `Startup.ConfigureServices` after adding services for authentication (<xref:Microsoft.Extensions.DependencyInjection.AuthenticationServiceCollectionExtensions.AddAuthentication%2A>) and the authentication handler for Identity Server (<xref:Microsoft.AspNetCore.Authentication.AuthenticationBuilderExtensions.AddIdentityServerJwt%2A>):
151+
152+
```csharp
153+
services.AddAuthentication()
154+
.AddIdentityServerJwt();
155+
services.TryAddEnumerable(
156+
ServiceDescriptor.Singleton<IPostConfigureOptions<JwtBearerOptions>,
157+
ConfigureJwtBearerOptions>());
158+
```
159+
116160
### Cookies vs. bearer tokens
117161

118162
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.

aspnetcore/tutorials/signalr-blazor-webassembly.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,8 +364,9 @@ To learn more about building Blazor apps, see the Blazor documentation:
364364

365365
> [!div class="nextstepaction"]
366366
> <xref:blazor/index>
367+
> [Bearer token authentication with Identity Server, WebSockets, and Server-Sent Events](xref:signalr/authn-and-authz#bearer-token-authentication)
367368
368369
## Additional resources
369370

370371
* <xref:signalr/introduction>
371-
* [SignalR cross-origin negotiation for authentication](xref:blazor/fundamentals/additional-scenarios#signalr-cross-origin-negotiation-for-authentication)
372+
* [SignalR cross-origin negotiation for authentication](xref:blazor/fundamentals/additional-scenarios#signalr-cross-origin-negotiation-for-authentication)

0 commit comments

Comments
 (0)