Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

websockets and authentication with identityserver4

I am using .net core 1.1 and identityserver 4 to get tokens and validate users. The web api works fine reading the bearer token from the headers and getting the user principal claims.

Now I want to use a websocket (not SignalR) for sending notifications. I can open a ws:// channel (or wss) but token isn't sent with the headers, so in the .net core application I have no information of the user (User Claims and Identity).

How can I authenticate the user through the websocket? I did a search but couldn't find any helpful information.

Thanks

like image 928
Harris Avatar asked Mar 31 '17 08:03

Harris


People also ask

Is IdentityServer4 obsolete?

IdentityServer4 support will last until the end of life of . NET Core 3.1 that means till November 2022. In that way, Duende provides new documentation for the fifth service version.

Do I need IdentityServer4?

Why do we need IdentityServer4? ASP.NET Identity can receive a security token from a third-party login provider like Facebook, Google, Microsoft and Twitter. But If you want to issue a security token for a local ASP.NET Identity user you need to work with a third-party library like IdentityServer4, OpenIddict.

What is the use of IdentityServer4?

IdentityServer is an authentication server that implements OpenID Connect (OIDC) and OAuth 2.0 standards for ASP.NET Core. It's designed to provide a common way to authenticate requests to all of your applications, whether they're web, native, mobile, or API endpoints.


1 Answers

There are two main problems related to the authentication in WebSocket middleware:

Authorization should be called manually

First of all, authorization is not applied to web socket request (as it is not a controller which can be marked with Authorize attribute). That's why in WebSocket middleware you need to call authorization by your self. This is easy to achieve by calling AuthenticateAsync extension method of the HttpContext object.

So, your middleware will be look something like this:

public class WebSocketMiddleware
{
    private readonly RequestDelegate next;
    public WebSocketMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        if (!context.WebSockets.IsWebSocketRequest)
        {
            await this.next.Invoke(context);
            return;
        }

        AuthenticateResult authenticateResult = 
            await context.AuthenticateAsync(OAuthValidationDefaults.AuthenticationScheme);

         ....
        });
    }

So, using authentication results you can check if the user is authenticated or not and then access authenticated user information.

Passing bearer token to web sockets request

For Web Socket connections, the default Authorization header does not work, because the WebSockets JS API doesn't allow setting custom parameters. To work around this limitation, the access token is passed quite often in the query string.

To make authentication middleware to use it, you need to update authentication validation options. This basically can be done in your startup script like this:

services
    .AddAuthentication()
    .AddOAuthValidation(options =>
    {
        options.Events = new OAuthValidationEvents
        {
            // Note: for Web Socket connections, the default Authorization header does not work,
            // because the WebSockets JS API doesn't allow setting custom parameters.
            // To work around this limitation, the access token is retrieved from the query string.
            OnRetrieveToken = context =>
            {
                context.Token = context.Request.Query["access_token"];
                return Task.FromResult(0);
            }
        };
    });

The following code can be used as an example to add access token to web socket url during connection initializing:

const protocol = location.protocol === "https:" ? "wss:" : "ws:";
const wsUri = protocol + "//" + window.location.host + "/ws" + "?access_token=" + token;
this.socket = new WebSocket(wsUri);
like image 103
Maxim Kornilov Avatar answered Oct 10 '22 07:10

Maxim Kornilov