From e0dd6b406fe2aeec9254c6010606f46e10e7b05e Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 3 Feb 2025 12:59:51 -0500 Subject: [PATCH 1/7] Impersonation for Windows Authentication --- aspnetcore/blazor/fundamentals/signalr.md | 66 +++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/aspnetcore/blazor/fundamentals/signalr.md b/aspnetcore/blazor/fundamentals/signalr.md index 04d41fd265a0..51aae9d21638 100644 --- a/aspnetcore/blazor/fundamentals/signalr.md +++ b/aspnetcore/blazor/fundamentals/signalr.md @@ -1511,6 +1511,72 @@ app.MapBlazorHub(); [!INCLUDE[](~/blazor/security/includes/httpcontext.md)] +## Impersonation for Windows Authentication + +Authenticated hub connections are typically created with to indicate the use of default credentials: + +```csharp +protected override async Task OnInitializedAsync() +{ + hubConnection = new HubConnectionBuilder() + .WithUrl(NavigationManager.ToAbsoluteUri("/hub"), config => + { + config.UseDefaultCredentials = true; + }) + .WithAutomaticReconnect() + .Build(); + + hubConnection.On("name", userName => + { + name = userName; + InvokeAsync(StateHasChanged); + }); + + await hubConnection.StartAsync(); +} +``` + +For more information, see . + +The preceding code is sufficient when the app is running in IIS Express as the signed-in user, which is likely a personal/work account under Windows Authentication. + +When the app is published to IIS, the app runs under the *Application Pool Identity*. The doesn't connect as the user that's accessing the page. The hub connects as the "user" IIS account hosting the app. + +Implement *impersonation* with the to use the identity of the browsing user. + +In the following example: + +* The user from the authentication state provider is cast to a . +* The identity's access token is passed to with the code that builds the . + +```csharp +protected override async Task OnInitializedAsync() +{ + var user = (WindowsIdentity) + (await AuthenticationStateProvider.GetAuthenticationStateAsync()) + .User.Identity; + + await WindowsIdentity.RunImpersonatedAsync(user.AccessToken, async () => + { + hubConnection = new HubConnectionBuilder() + .WithUrl(NavigationManager.ToAbsoluteUri("/hub"), config => + { + config.UseDefaultCredentials = true; + }) + .WithAutomaticReconnect() + .Build(); + + hubConnection.On("name", userName => + { + name = userName; + InvokeAsync(StateHasChanged); + }); + + await hubConnection.StartAsync(); + }); +} +``` + ## Additional server-side resources * [Server-side host and deployment guidance: SignalR configuration](xref:blazor/host-and-deploy/server#signalr-configuration) From f3f55c16952e33b9539161ddffe46285fc7ea55b Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 3 Feb 2025 13:08:46 -0500 Subject: [PATCH 2/7] Updates --- aspnetcore/blazor/fundamentals/signalr.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aspnetcore/blazor/fundamentals/signalr.md b/aspnetcore/blazor/fundamentals/signalr.md index 51aae9d21638..7c1ddfbcdf9c 100644 --- a/aspnetcore/blazor/fundamentals/signalr.md +++ b/aspnetcore/blazor/fundamentals/signalr.md @@ -1513,7 +1513,7 @@ app.MapBlazorHub(); ## Impersonation for Windows Authentication -Authenticated hub connections are typically created with to indicate the use of default credentials: +Authenticated hub connections () are typically created with to indicate the use of default credentials for HTTP requests: ```csharp protected override async Task OnInitializedAsync() @@ -1540,14 +1540,14 @@ For more information, see . The preceding code is sufficient when the app is running in IIS Express as the signed-in user, which is likely a personal/work account under Windows Authentication. -When the app is published to IIS, the app runs under the *Application Pool Identity*. The doesn't connect as the user that's accessing the page. The hub connects as the "user" IIS account hosting the app. +When the app is published to IIS, the app runs under the *Application Pool Identity*. The hub connection doesn't connect as the user accessing the page. The hub connects as the IIS "user" account hosting the app. -Implement *impersonation* with the to use the identity of the browsing user. +Implement *impersonation* with the hub to use the identity of the browsing user. In the following example: * The user from the authentication state provider is cast to a . -* The identity's access token is passed to with the code that builds the . +* The identity's access token is passed to with the code that builds and starts the hub. ```csharp protected override async Task OnInitializedAsync() From 6e2b30a3bdf6532aaddb1700717b18962fda5e40 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 3 Feb 2025 13:16:02 -0500 Subject: [PATCH 3/7] Updates --- aspnetcore/blazor/fundamentals/signalr.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aspnetcore/blazor/fundamentals/signalr.md b/aspnetcore/blazor/fundamentals/signalr.md index 7c1ddfbcdf9c..6ba2d496c9dd 100644 --- a/aspnetcore/blazor/fundamentals/signalr.md +++ b/aspnetcore/blazor/fundamentals/signalr.md @@ -1536,11 +1536,11 @@ protected override async Task OnInitializedAsync() } ``` -For more information, see . +For more information, see . The preceding code is sufficient when the app is running in IIS Express as the signed-in user, which is likely a personal/work account under Windows Authentication. -When the app is published to IIS, the app runs under the *Application Pool Identity*. The hub connection doesn't connect as the user accessing the page. The hub connects as the IIS "user" account hosting the app. +When the app is published to IIS, the app runs under the *Application Pool Identity*. The hub connects as the IIS "user" account hosting the app, not the user accessing the page. Implement *impersonation* with the hub to use the identity of the browsing user. From 598a2d7ededd3e6301564d10fb47377c2ebb8bf5 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 3 Feb 2025 13:19:29 -0500 Subject: [PATCH 4/7] Updates --- aspnetcore/blazor/fundamentals/signalr.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/blazor/fundamentals/signalr.md b/aspnetcore/blazor/fundamentals/signalr.md index 6ba2d496c9dd..72149242d2e9 100644 --- a/aspnetcore/blazor/fundamentals/signalr.md +++ b/aspnetcore/blazor/fundamentals/signalr.md @@ -1513,7 +1513,7 @@ app.MapBlazorHub(); ## Impersonation for Windows Authentication -Authenticated hub connections () are typically created with to indicate the use of default credentials for HTTP requests: +Authenticated hub connections () are created with to indicate the use of default credentials for HTTP requests: ```csharp protected override async Task OnInitializedAsync() From e826f34c7d43fc42fe14df114ae5e073f3a530e2 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 3 Feb 2025 13:20:28 -0500 Subject: [PATCH 5/7] Updates --- aspnetcore/blazor/fundamentals/signalr.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/blazor/fundamentals/signalr.md b/aspnetcore/blazor/fundamentals/signalr.md index 72149242d2e9..e0975c55f5cc 100644 --- a/aspnetcore/blazor/fundamentals/signalr.md +++ b/aspnetcore/blazor/fundamentals/signalr.md @@ -1538,7 +1538,7 @@ protected override async Task OnInitializedAsync() For more information, see . -The preceding code is sufficient when the app is running in IIS Express as the signed-in user, which is likely a personal/work account under Windows Authentication. +The preceding code is sufficient when the app is running in IIS Express as the signed-in user under Windows Authentication, which is likely the user's personal or work account. When the app is published to IIS, the app runs under the *Application Pool Identity*. The hub connects as the IIS "user" account hosting the app, not the user accessing the page. From 95b2159add425af227f9ad713a1132f425543dfd Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 3 Feb 2025 14:08:47 -0500 Subject: [PATCH 6/7] Updates --- aspnetcore/blazor/fundamentals/signalr.md | 74 +++++++++-------------- 1 file changed, 30 insertions(+), 44 deletions(-) diff --git a/aspnetcore/blazor/fundamentals/signalr.md b/aspnetcore/blazor/fundamentals/signalr.md index e0975c55f5cc..b89c5212eda6 100644 --- a/aspnetcore/blazor/fundamentals/signalr.md +++ b/aspnetcore/blazor/fundamentals/signalr.md @@ -1513,32 +1513,9 @@ app.MapBlazorHub(); ## Impersonation for Windows Authentication -Authenticated hub connections () are created with to indicate the use of default credentials for HTTP requests: +Authenticated hub connections () are created with to indicate the use of default credentials for HTTP requests. For more information, see . -```csharp -protected override async Task OnInitializedAsync() -{ - hubConnection = new HubConnectionBuilder() - .WithUrl(NavigationManager.ToAbsoluteUri("/hub"), config => - { - config.UseDefaultCredentials = true; - }) - .WithAutomaticReconnect() - .Build(); - - hubConnection.On("name", userName => - { - name = userName; - InvokeAsync(StateHasChanged); - }); - - await hubConnection.StartAsync(); -} -``` - -For more information, see . - -The preceding code is sufficient when the app is running in IIS Express as the signed-in user under Windows Authentication, which is likely the user's personal or work account. +When the app is running in IIS Express as the signed-in user under Windows Authentication, which is likely the user's personal or work account, the default credentials are those of the signed-in user. When the app is published to IIS, the app runs under the *Application Pool Identity*. The hub connects as the IIS "user" account hosting the app, not the user accessing the page. @@ -1552,31 +1529,40 @@ In the following example: ```csharp protected override async Task OnInitializedAsync() { - var user = (WindowsIdentity) - (await AuthenticationStateProvider.GetAuthenticationStateAsync()) - .User.Identity; + var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); - await WindowsIdentity.RunImpersonatedAsync(user.AccessToken, async () => + if (authState?.User.Identity is not null) { - hubConnection = new HubConnectionBuilder() - .WithUrl(NavigationManager.ToAbsoluteUri("/hub"), config => - { - config.UseDefaultCredentials = true; - }) - .WithAutomaticReconnect() - .Build(); - - hubConnection.On("name", userName => - { - name = userName; - InvokeAsync(StateHasChanged); - }); + var user = authState.User.Identity as WindowsIdentity; - await hubConnection.StartAsync(); - }); + if (user is not null) + { + await WindowsIdentity.RunImpersonatedAsync(user.AccessToken, + async () => + { + hubConnection = new HubConnectionBuilder() + .WithUrl(NavManager.ToAbsoluteUri("/hub"), config => + { + config.UseDefaultCredentials = true; + }) + .WithAutomaticReconnect() + .Build(); + + hubConnection.On("name", userName => + { + name = userName; + InvokeAsync(StateHasChanged); + }); + + await hubConnection.StartAsync(); + }); + } + } } ``` +In the preceding code, `NavManager` is a , and `AuthenticationStateProvider` is an service instance ([`AuthenticationStateProvider` documentation](xref:blazor/security/authentication-state)). + ## Additional server-side resources * [Server-side host and deployment guidance: SignalR configuration](xref:blazor/host-and-deploy/server#signalr-configuration) From 7ea311b2f425f4fcb659a01f0408419d36146119 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Mon, 3 Feb 2025 14:56:49 -0500 Subject: [PATCH 7/7] Apply suggestions from code review Co-authored-by: Brennan --- aspnetcore/blazor/fundamentals/signalr.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aspnetcore/blazor/fundamentals/signalr.md b/aspnetcore/blazor/fundamentals/signalr.md index b89c5212eda6..40fcfde1b905 100644 --- a/aspnetcore/blazor/fundamentals/signalr.md +++ b/aspnetcore/blazor/fundamentals/signalr.md @@ -1517,14 +1517,14 @@ Authenticated hub connections ( connects as the IIS "user" account hosting the app, not the user accessing the page. -Implement *impersonation* with the hub to use the identity of the browsing user. +Implement *impersonation* with the to use the identity of the browsing user. In the following example: * The user from the authentication state provider is cast to a . -* The identity's access token is passed to with the code that builds and starts the hub. +* The identity's access token is passed to with the code that builds and starts the . ```csharp protected override async Task OnInitializedAsync()