-
Notifications
You must be signed in to change notification settings - Fork 686
Unauthorized (401) during websocket handshake when authorizing SignalR client with JWT bearer token #2349
Comments
I managed to replace the whole authentication handling mechanism and figured out what was happening. So what I did finally is replaced the token retrieval mechanism and incorporated my own "protocol". I needed to know some constants using by IdentityServer which I got from the source code. However, I would be happy to hear about a better solution. This is my final code and that is working in every tested cases, which does not mean it would work in all circumstances. The code that replaces the token retrieval mechanism:
And the custom token retrieval implementation
I hope that can be help to someone. I am still not sure whether this behavior is expected or it is a bug in IdentityServer or in Asp.NET Core. |
IdentityServerAuthenticationOptions has the SupportedTokens default to SupportedTokens.Both, I believe if it is set to SupportedTokens.Reference only, the JwtBearerEvents.OnMessageReceived won't even fired. From the IdentityServerAuthenticationHandler.HandleAuthenticateAsync, it needs to use the TokenRetriever to retrieve the token first and check if the token is JWT and if Options.SupportsJwt is true before letting the the JwtBearerHandler to handle it and fire the JwtBearerEvents. I think using the TokenRetriever is the right way, but let Dominick comments on it. However, from the spec, it should NOT pass the token in the query string parameter:
|
Thanks for your answer. I am not satisfied with the query string approach either. However, according to this thread there seems to be no other option for websocket handshake. It goes over HTTPS so the only concern is that it can be logged during the way. It is maybe possible to initiate an immediate token refresh after the handshake. As long as the websocket channel is up it does not need to reauthorize. I am open to suggestions, please keep me posted. Thx |
As stated by https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#The_WebSocket_Handshake,
For my case, the token is passed from the JS client in cookie to the server and the server can see the cookie from the ws http(s) handshake request to do authentication and establish the WS connection. Why do you close this issue if you want others to reply it? I am not sure if they will check the closed issue. I would like to hear their comments too. |
Thanks for the info. We use a HttpInterceptor to pass the bearer token during standard API calls, but that interceptor mechanism is not called during the SignalR handshake. I have not enough experience on the front end side to figure out how to put the actual bearer token into the header. According to your link I understand that might be possible. However what I find everywhere is that for bearer tokens the query string approach is the standard. See PatrickJS/angular-websocket#15 for angular-websocket. Could you please point out some implementation on how to intercept the websocket handshake on the client side (JS or TS)? |
Sorry, I have not set the authorization header from the client js before. I just see the spec saying it MAY contain the Authorization header:
From the link you shown, it seems it cannot and people suggests using querystring or basic authentication. For my case, the server put the token in the cookie and the client will send the cookie in the ws request. |
All set on this issue -- can we close? |
Thank you for the support. I am closing the issue. The custom token retriever approach seems to be a working solution for me - it works in production now. |
Hi, I'm having the exact same issue as @danielleiszen is having. I've tried multiple solutions but couldn't get it to work through SignalR. The CustomTokenRetriever.FromHeaderAndQueryString method gets hit when making a webrequest to a controller that need authorization but not when trying to connect to a signalr hub. This is my code:
So my question is, which transport types are you using for SignalR and is it possible to share your SignalR settings? Got it to work with normal JWT but not with ID4. |
I have the same issue @LodeKennes |
@LodeKennes use app.UseAuthentication(); before the app.UseSignalR() and it works |
@danielleiszen let me put my two cents to this. I think most of us store tokens in cookies and during WebSockets handshake they are also sent to the server, so I suggest using token retrieval from cookie. To do this add this below last
Actually we could delete retrieval from query string at all as according to Microsoft docs this is not truly secure and can be logged somewhere. |
I'm recently migrating from .Net core 2.1 to 3.0, and experienced getting 401's during signalr websocket negotiate handshakes. What fixed this for me was adding options.LegacyAudienceValidation = true; to the AddIdentityServerAuthentication ConfigureServices parts in addition to ensuring that app.UseAuthorization() was between UseRouting and UseEndpoints in the Configure part. Figured I'd comment here as there may be other folks coming across this while upgrading to .net core 3.0 and 3.1 like me. If the UseAuthorization() call is not in the correct spot of the pipeline, then the TokenRetriever isn't receiving the auth/bearer token during the websocket handshake. Startup looks like this after porting to .net core 3.0: ConfigureServices
Configure:
|
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
I am not sure if it is a problem with IdentityServer or ASP.Net identity or maybe I am missing something. However, I think I am not doing anything wrong and according to the examples it should work this way. I already posted my problem on stackoverflow.
I have two services: aspnet-core IdentityServer 4, and aspnet-core web API on the server side, Angular4 on the client side. The SignalR hub is hosted by the web API. Without authorization everything works fine. However, I need authorization on the SignalR hub.
When I put the [Authorize] attribute on the hub, I get 401 for the negotiation request with SignalR. I send the access_token in the querystring to the API and on the server side I extract and set the token for the request so the authorization pipeline could use it. The token validation is successful and regardless I get the unauthorized error.
On the API side I get the following log messages
The IdentityServer logs (after the time of the token validation)
According to this issue I suspected that the problem was with the authentication schemes so I set every schema for what I think was appropriate. It did not help.
The text was updated successfully, but these errors were encountered: