Skip to content
This repository has been archived by the owner on Dec 18, 2018. It is now read-only.

Authorize attribute doesn't work with SignalR ASP.Net Core 2.1 #1764

Closed
redplane opened this issue Mar 29, 2018 · 9 comments
Closed

Authorize attribute doesn't work with SignalR ASP.Net Core 2.1 #1764

redplane opened this issue Mar 29, 2018 · 9 comments

Comments

@redplane
Copy link

Hi,

I've follow getting started with SignalR ASP.NET Core 2.1. My application worked fine until I put [Authorize] attribute to my hub.

I created one Authentication requirement:

public class SolidAccountRequirementHandler : AuthorizationHandler<SolidAccountRequirement>
{
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SolidAccountRequirement requirement)
        {
            context.Succeed(requirement);
            return Task.CompletedTask;
        }
 }

In my Startup.cs, I added these code blocks:

services.AddSingleton<IAuthorizationHandler, SolidAccountRequirementHandler>();

            services.AddCors(
                options => options.AddPolicy("AllowCors",
                    builder =>
                    {
                        builder
                            .AllowAnyOrigin()
                            .AllowCredentials()
                            .AllowAnyHeader()
                            .AllowAnyMethod();
                    })
            );
            
            services.AddAuthorization(x =>
            {
                x.AddPolicy("MainPolicy", builder =>
                {
                    builder.Requirements.Add(new SolidAccountRequirement());
                });
            });

            services.AddSignalR();
app.UseSignalR(routes =>
            {
                routes.MapHub<ChatHub>("/chathub");
            });

In my hub, I put my Authorize attribute like this:

public class ChatHub : Hub
    {
        [Authorize(Policy = "MainPolicy")]
        public override Task OnConnectedAsync()
        {
            var a = 1;
            return base.OnConnectedAsync();
        }
    }

What I expected was SolidRequirementHandler would be called, but it jumped straight into OnConnectedAsync method of ChatHub.cs.

Am I doing anything wrong ?

Thank you,

@davidfowl
Copy link
Member

You need to put it on the Hub itself, not the OnConnectedAsync method.

@redplane
Copy link
Author

Thank you.

I think this issue can be closed.

@ddydeveloper
Copy link

ddydeveloper commented Oct 26, 2018

Hi, @davidfowl

I've configured an authorization flow for my Asp.Net core app and use the [Authorize] attribute for the SignalR hub:

[Authorize]
public class NotificationsHub : Hub<INotificationsHub>
{
    public override async Task OnConnectedAsync()
    {
        await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception exception)
    {
        await base.OnDisconnectedAsync(exception);
    }
}

OnConnectedAsync method works fine, I can see all user claims according to an access_token, but when I've decided to check network I have found the following error in console:

WebSocket connection to 'ws://localhost:1264/hubs/notifications?id=my_id&access_token=my_token' failed: HTTP Authentication failed; no valid credentials available

Another problem, OnDisconnectedAsync method will never be called, even if I closed the browser tab.

image

The token is provided from the client angular app:

this._hubConnection = new signalR.HubConnectionBuilder()
    .withUrl(`${environment.apiUrl}hubs/notifications`, { accessTokenFactory: () => access_token })
    .configureLogging(signalR.LogLevel.Warning)
    .build();

And the access_token looks like:

Authorization: Bearer just_example_token_to_authenticate_hub_user

Not sure but maybe it is because in the GET request contains token without schema "Bearer":

image

@analogrelay
Copy link
Contributor

analogrelay commented Oct 26, 2018

Not sure but maybe it is because in the GET request contains token without schema "Bearer"

This is expected. Browsers don't allow us to encode the access_token in the standard Authorization HTTP header, so we have to use the query string. When using the query string we just put it in access_token query string value, so the Authorization header isn't required. Have you configured your server to accept the token in the query string as described in our docs on Bearer Token Authentication in SignalR?

@ddydeveloper
Copy link

ddydeveloper commented Oct 26, 2018

@anurse I've investigated more and define, that WebSockets and ServerSentEvents transports are failed to start because of this:

image

I didn't use method AddJwtBearer in startup class, authentication is configured by the following code:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddOAuthValidation();

I've tried to add AddJwtBearer to the services but after all my API queries return 500 error, suppose because of the different validation schema

@ddydeveloper
Copy link

ddydeveloper commented Oct 26, 2018

@anurse Solved this problem by the following code:

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddOAuthValidation(options =>
{
    options.Events.OnRetrieveToken = context =>
    {
        context.Token = context.Request.Query["access_token"];
        return Task.CompletedTask;
    };
});

Actually the same as described in the documentation you have referenced. Thank you for help. Close the issue!

@analogrelay
Copy link
Contributor

Yeah, OAuthValidation is a third-party authentication system so we don't have direct information about them in our documentation. Our documentation refers to our built-in JWT validation system

@analogrelay
Copy link
Contributor

One important note: It is very strongly recommended that you only use the query string when the request is for your SignalR endpoint. Notice that the docs have code to check based on the path before reading from the query string. Your sample code reads from the access_token field unconditionally.

I recommend you restrict this based on path and/or only do it if the request is a WebSocket (which you can check easily by checking that the Upgrade header is present and has the value websocket)

@ddydeveloper
Copy link

@anurse sure, it is just a base implementation. I'll provide check hub path logic. Thank you a lot!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants