Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create custom authentication mechanism based on HTTP header?

I'm leaving old version of question on a bottom.

I'd like to implement custom authentication for SignalR clients. In my case this is java clients (Android). Not web browsers. There is no Forms authentication, there is no Windows authentication. Those are plain vanilla http clients using java library.

So, let's say client when connects to HUB passes custom header. I need to somehow authenticate user based on this header. Documentation here mentions that it is possible but doesn't give any details on how to implement it.

Here is my code from Android side:

hubConnection = new HubConnection("http://192.168.1.116/dbg", "", true, new NullLogger());
        hubConnection.getHeaders().put("SRUserId", userId);
        hubConnection.getHeaders().put("Authorization", userId);

        final HubProxy hubProxy = hubConnection.createHubProxy("SignalRHub");
        hubProxy.subscribe(this);


        // Work with long polling connections only. Don't deal with server sockets and we
        // don't have WebSockets installed
        SignalRFuture<Void> awaitConnection = hubConnection.start(new LongPollingTransport(new NullLogger()));
        try
        {
            awaitConnection.get();

            Log.d(LOG_TAG, "------ CONNECTED to SignalR -- " + hubConnection.getConnectionId());
        }
        catch (Exception e)
        {
            LogData.e(LOG_TAG, e, LogData.Priority.High);
        }

P.S. Original question below was my desire to "simplify" matter. Because I get access to headers in OnConnected callback. I thought there is easy way to drop connection right there..


Using Signal R with custom authentication mechanism. I simply check if connecting client has certain header passed in with connection request.

Question is - how do I DECLINE or NOT connect users who don't pass my check? Documentation here doesn't really explain such scenario. There is mentioning of using certificates/headers - but no samples on how to process it on server. I don't use Forms or windows authentication. My users - android java devices.

Here is code from my Hub where I want to reject connection..

public class SignalRHub : Hub
{
    private const string UserIdHeader = "SRUserId";

    private readonly static SignalRInMemoryUserMapping Connections = new SignalRInMemoryUserMapping();

    public override Task OnConnected()
    {
        if (string.IsNullOrEmpty(Context.Headers[UserIdHeader]))
        {
            // TODO: Somehow make sure SignalR DOES NOT connect this user!
            return Task.FromResult(0);
        }

        Connections.Add(Context.Headers[UserIdHeader], Context.ConnectionId);
        Debug.WriteLine("Client {0}-{1} - {2}", Context.Headers[UserIdHeader], Context.ConnectionId, "CONNECTED");

        return base.OnConnected();
    }
like image 341
katit Avatar asked Jul 29 '16 16:07

katit


People also ask

How do I set basic authentication in HTTP header?

Basic Auth: The client sends HTTP requests with the Authorization header that contains the word Basic, followed by a space and a base64-encoded(non-encrypted) string username: password. For example, to authorize as username / Pa$$w0rd the client would send. Note: Base64 encoding does not mean encryption or hashing!

Does HTTP headers support authentication?

HTTP WWW-Authenticate header is a response-type header. It serves as a support for various authentication mechanisms which are important to control access to pages and other resources as well. All of these mechanisms are based on the use of the 401 status code.

What is HTTP header based authentication?

Legacy applications commonly use Header-based authentication. In this scenario, a user (or message originator) authenticates to an intermediary identity solution. The intermediary solution authenticates the user and propagates the required Hypertext Transfer Protocol (HTTP) headers to the destination web service.

How do you implement custom authentication?

You have to create a login page for end user and your client app will send the user's credentials as well as your client system credentials (use HTTP Basic Authentication for client system credentials) to authorization server to get the token.


2 Answers

So I just created a custom Authorization Attribute and overrode the AuthorizeHubConnection method to get access to the request and implemented the logic that you were trying to do with the Header and it appears to be working.

using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;

namespace SignalR.Web.Authorization
{
    public class HeadersAuthAttribute : AuthorizeAttribute
    {
        private const string UserIdHeader = "SRUserId";

        public override bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request)
        {
            if (string.IsNullOrEmpty(request.Headers[UserIdHeader]))
            {
                return false;
            }

            return true;
        }
    }
}

Hub

 [HeadersAuth]
    [HubName("messagingHub")]
    public class MessagingHub : Hub
    {

    }

Which yields this in the console (if the picture doesn't show up, it's a [Failed to load resource: the server responded with a status of 401 (Unauthorized)]):

enter image description here

like image 103
Mark C. Avatar answered Sep 30 '22 04:09

Mark C.


In fact, accepted answer is wrong. Authorization attribute, surprisingly, shall be used for authorization (that is, you should use it for checking whether requesting authenticated user is authorized to perform a desired action).

Also, since you using incorrect mechanics, you don't have HttpContext.Current.User.Identity set. So, you have no clear way to pass user info to your business / authorization logic.

And third, doing that you won't be able to use Clients.User() method to send message to specific user, since SignalR will be not able to map between users and connections.

The correct way is to plug in into OWIN authentication pipeline. Here is an excellent article explaining and demonstrating in detail how to implement custom authentication to be used in OWIN.

I not going to copy-paste it here, just follow it and make sure you implement all required parts:

  • Options
  • Handler
  • Middleware

After you have these, register them into OWIN:

app.Map("/signalr", map =>
{
   map.UseYourCustomAuthentication();

   var hubConfiguration = new HubConfiguration
   {
       Resolver = GlobalHost.DependencyResolver,
   };

   map.RunSignalR(hubConfiguration);
});
like image 30
Illidan Avatar answered Sep 30 '22 04:09

Illidan