dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.15k stars 9.92k forks source link

Blazor: Authorization taking too much time makes the component lose its state #50834

Open Kiritsu opened 11 months ago

Kiritsu commented 11 months ago

Is there an existing issue for this?

Describe the bug

Context: Blazor Server-Side on .NET 8 RC1

I have a my own IAuthorizationHandler: it pulls the user's permissions from the database and then validate the different pending requirements of a specific type to validate a policy. (it is triggered by the @attribute [Authorize(Policy = "Root")] on the razor page)

When a client lose connection with the server, the overlay Attempting to reconnect to the server: N of M appears:

image

When the client gains back the connection with the server, the same Circuit is maintained successfully and the authorization is validated again.

When the process of the IAuthorizationHandler is very fast, the component will resume with its state successfully:

msedge_qx0mIIpW8p

When the process of the IAuthorizationHandler is taking too "long", the component will be unloaded, replaced with Authorizing... when reconnecting, then after authorization is successful, the component will be reloaded with a brand new state, which leads to losing the data that was on the page before losing connection with the server.

msedge_HSVumEpB7Y

Expected Behavior

In my opinion, there is multiple expected behavior:

Steps To Reproduce

  1. Configure a simple policy Root with a custom requirement
  2. Implement a IAuthorizationHandler with a huge delay on it that Succeed your pending custom requirement
  3. Open your DevTools on your browser
  4. Open a razor page that has a @attribute [Authorize(Policy = "Root")]
  5. Go offline from your DevTools to lose connection with Blazor Server
  6. Wait for the "Attempting to reconnect to the server..."
  7. Go online again from your DevTools to make SignalR reconnect successfully to Blazor Server

And now, with a big delay, you should see the component disappear, having Authorizing... appear, then the component reloading with its default state.

Exceptions (if any)

No exceptions.

.NET Version

8.0.100-rc.1.23455.8

Anything else?

Microsoft Visual Studio Community 2022 (64-bit) - Preview Version 17.8.0 Preview 2.0

.NET SDK: Version: 8.0.100-rc.1.23455.8 Commit: e14caf947f

Runtime Environment: OS Name: Windows OS Version: 10.0.22621 OS Platform: Windows RID: win-x64 Base Path: C:\Program Files\dotnet\sdk\8.0.100-rc.1.23455.8\

.NET workloads installed: There are no installed workloads to display.

Host: Version: 8.0.0-rc.1.23419.4 Architecture: x64 Commit: 92959931a3 RID: win-x64

.NET SDKs installed: 7.0.203 [C:\Program Files\dotnet\sdk] 8.0.100-preview.6.23330.14 [C:\Program Files\dotnet\sdk] 8.0.100-rc.1.23455.8 [C:\Program Files\dotnet\sdk]

.NET runtimes installed: Microsoft.AspNetCore.App 6.0.22 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 7.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 7.0.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 8.0.0-preview.6.23329.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 8.0.0-rc.1.23421.29 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.21 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.22 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 7.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 7.0.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 8.0.0-preview.6.23329.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 8.0.0-rc.1.23419.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.22 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 7.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 7.0.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 8.0.0-preview.6.23329.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 8.0.0-rc.1.23420.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other architectures found: x86 [C:\Program Files (x86)\dotnet] registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]

Environment variables: Not set

global.json file: Not found

javiercn commented 11 months ago

@Kiritsu thanks for contacting us.

The challenge is that the other behavior is incorrect and leads to security problems, as you can imagine that if the security changed during the reconnection and we redisplayed the UI, you could be in a situation where you could take an action you aren't allowed to.

Kiritsu commented 11 months ago

Hello, thanks for your quick reply!

Could we just not wait that the authorization is "done" before unloading the component though? Or keep it somewhere until the task of authorizing is done?

and we redisplayed the UI

Technically, the UI is never gone, it is just overlayed by the "Attempting to reconnect" which you can even remove if you want. It just temporarily disappear when the authorization is taking too much time. In my opinion, it could just stay as it is (maybe frozen or something?):

What's the "delay" before the component state is lost in that case then?

On my end, for now, since my CircuitHandler is triggered when the connection is re-established, I already pull the latest permissions from my database so I don't need to do it again in my IAuthorizationHandler.

However, this lead to a new security issue, since it will only update permissions when a Circuit is "created" or "resumed". To face this issue, I added a property on my UserService which gets the last time the permissions for this user were pulled from the database.

In my IAuthorizationHandler, I now have a delta of 30 seconds, which is the maximum acceptable amount of delay to mark the user's permissions as outdated. In that case, I'll pull them from the database directly from the IAuthorizationHandler (so it will take longer and it will probably flicker my component with an Authorizing...). This case should never happen after the client reconnected to the Blazor server, but only while navigating normally to the website.

Do you think there could be a better workaround to not lose my component's state? My use-case is that my app is being used on mobile phones (on the browser). It is an app where you have to fill pretty long forms, and you sometimes have to open another app (Notes) to retrieve some information, or switch to your camera to take pictures and give them to the form.

When you're not on the browser, it will pause the Websocket connections, leading to disconnections from the Blazor Server. And resuming the connection would entirely reset my form :)

ghost commented 11 months ago

Thanks for contacting us.

We're moving this issue to the .NET 9 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s). If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

ghost commented 8 months ago

Thanks for contacting us.

We're moving this issue to the .NET 9 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s). If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

ghost commented 8 months ago

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.