Skip to content

Initializing Azure SignalR using UseAzureSignalR doesn't facilitate examining Authorization metadata soon enough like UseEndpoints does. #1602

@mhintzke

Description

@mhintzke

Describe the bug

Using UseAzureSignalR does not inspect the Hub objects for AuthorizeAttributes prior to making the /negotiate call like ASP .NET Core SignalR does. This results in Negotiation authorization to behave incorrectly. Authorization for Azure SignalR only takes place between the negotiate calls and the redirection to Azure SignalR, but if Authorization to /negotiate fails, then the Authorization check against the Hubs can never happen.

How does it work in ASP .NET SignalR?

In ASP .NET Core SignalR, after a hub is started and a negotiation takes place, the DefaultHubDispatcher calls a method called IsHubMethodAuthorized here:

https://github.com/dotnet/aspnetcore/blob/0ee742c53f2669fd7233df6da89db5e8ab944585/src/SignalR/server/Core/src/Internal/DefaultHubDispatcher.cs#L568

This uses the HubMethodDescriptor.Policies to determine if the user is Authorized to perform negotiation. Those same HubMethodDescriptor.Policies are accumulated using the following code:

https://github.com/dotnet/aspnetcore/blob/0ee742c53f2669fd7233df6da89db5e8ab944585/src/SignalR/server/Core/src/Internal/DefaultHubDispatcher.cs#L658

This looks at the Hubs, and grabs the AuthorizeAttributes off each to build up a list of IAuthorizeData (policies).

This allows ASP .NET Core to properly handle Authorization of Hubs exactly like Authorization of standard controller endpoints. This logic should probably be used in Azure SignalR as well so that policies can be enforced (or not enforced) in the same way.

I will add, I think the primary place that this metadata gets added to the /negotiate endpoints is right here:

https://github.com/dotnet/aspnetcore/blob/de3019b7dab5b5995942db1fbec6aa466c4af34c/src/SignalR/common/Http.Connections/src/ConnectionEndpointRouteBuilderExtensions.cs#L118

To Reproduce

See this forked branch -> https://github.com/mhintzke/azure-signalr/tree/matt/reproduce-1602

Just add a connection string and then toggle ShouldUseAzureSignalR app setting between true and false and load the app. You will see that when using Azure SignalR we get a 401 on the /negotiate (because the FallbackPolicy is used) but on ASP .NET SignalR we get a 200.


Simply setup a FallbackPolicy that will always fail authorization and a "foobar" policy (it can do anything, but you can set a breakpoint on this and see that it is never be invoked) using native ASP .NET AddAuthorization method. Then, add [Authorize(Policy = "foobar")] to a Hub and try to negotiate with it. You will see that rather than using "foobar" to Authorize, it uses the FallbackPolicy and will therefore fail Authorization. Setting FallbackPolicy back to null then allows the /negotiate endpoint to pass Authorization upstream from Azure SignalR endpoint and then the Hub authorizes correctly.

Further technical details

  • Azure SignalR v1.17.0 (also reproduced on 1.15.0)
  • .NET 6 (but will reproduce in .NET 5 since .NET 6 doesn't seem to be officially supported)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions