Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SignalR dotnet core authentication

I am using Microsoft.AspNetCore.SignalR nuget package with Bazinga.AspNetCore.Authentication.Basic which adds basic authentication to dotnet core. My C# SignalR client connects when there is no authentication, but when I add AuthorizeAttribute it connects by http and http request header gets authenticated successfully but the Socket does not authenticate probably because there is no header in socket messages.

So I am wondering how should I pass a token or something to authenticated socket connection or is there a example code that I can follow. I think I should pass a random token to just authenticated user and the user needs to constantly pass the token in messages.

Client project, Server project

Server:

using System.Threading.Tasks;
using Bazinga.AspNetCore.Authentication.Basic;
using Domainlogic;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;

namespace API
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddCors(options => options.AddPolicy("CorsPolicy", builder =>
            {
                builder
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowAnyOrigin();
            }));

            services.AddSignalR();

            services.AddAuthentication(BasicAuthenticationDefaults.AuthenticationScheme)
                .AddBasicAuthentication(credentials => Task.FromResult(
                    credentials.username == "SomeUserName"
                    && credentials.password == "SomePassword"));
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseCors("CorsPolicy");

            app.UseCors(CorsConstants.AnyOrigin);

            app.UseFileServer();

            app.UseSignalR(route => { route.MapHub<MessageHub>("/chat"); });

            app.UseAuthentication();
        }
    }
}

Server hub:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;

namespace Domainlogic
{
    public class MessagePayload
    {
        public string Name { get; set; }

        public string Message { get; set; }

        public DateTime Date { get; set; }        
    }

    [Authorize]
    public class MessageHub : Hub
    {   
        // connected IDs
        private static readonly HashSet<string> ConnectedIds = new HashSet<string>();

        public override async Task OnConnectedAsync()
        {
            ConnectedIds.Add(Context.ConnectionId);

            await Clients.All.SendAsync("SendAction", "joined", ConnectedIds.Count);
        }

        public override async Task OnDisconnectedAsync(Exception ex)
        {
            ConnectedIds.Remove(Context.ConnectionId);

            await Clients.All.SendAsync("SendAction", "left", ConnectedIds.Count);
        }

        public async Task Send(MessagePayload message)
        {
            await Clients.All.SendAsync("SendMessage", message);
        }
    }
}

Client:

using System;
using System.Net;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Connections.Client;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Logging;

namespace SignalRClient
{
    public class MessagePayload
    {
        public string Name { get; set; }

        public string Message { get; set; }

        public DateTime Date { get; set; }        
    }

    class Program
    {
        public static string Base64Encode(string plainText) {
            var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
            return System.Convert.ToBase64String(plainTextBytes);
        }

        static void Main(string[] args)
        {
            var credential = Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes("SomeUserName" + ":" + "SomePassword"));

            //Set connection
            var connection = new HubConnectionBuilder()
                .WithUrl("http://localhost:5000/chat", options =>
                {
                    options.Headers.Add("Authorization", $"Basic {credential}");
                })
                .AddJsonProtocol()
                .Build();

            connection.On<MessagePayload>("SendMessage", param =>
            {
                Console.WriteLine(param.Message);
            });

            connection.StartAsync().Wait();

            var startTimeSpan = TimeSpan.Zero;
            var periodTimeSpan = TimeSpan.FromSeconds(3);
            int i = 0;

            var timer = new System.Threading.Timer((e) =>
            {
                connection.InvokeAsync<MessagePayload>("Send", new MessagePayload()
                {
                    Message = "Some message: " + i++
                });
            }, null, startTimeSpan, periodTimeSpan);


            Console.Read();
            connection.StopAsync();
        }
    }
}
like image 940
Node.JS Avatar asked May 18 '18 14:05

Node.JS


People also ask

How do you implement authentication in SignalR?

When using the browser client, no additional configuration is needed. If the user is logged in to your app, the SignalR connection automatically inherits this authentication. Cookies are a browser-specific way to send access tokens, but non-browser clients can send them.

How can I secure my SignalR?

If you want to restrict access to it you need to authenticate users and authorize their actions. You authenticate using standard web auth methods (forms auth, cookies, Windows auth, etc.) and you can authorize in code using SignalR constructs (like the Authorize attribute you point out) or with your own code.

Does SignalR need SSL?

If your SignalR application transmits sensitive information between the client and server, use SSL for the transport.

Does SignalR use JSON?

ASP.NET Core SignalR supports two protocols for encoding messages: JSON and MessagePack. Each protocol has serialization configuration options.


1 Answers

Thanks to "davidfowl" on GitHub, the solution was moving UseAuthentication above the UseSignalR.

Source: https://github.com/aspnet/SignalR/issues/2316

Instead of:

app.UseSignalR(route => { route.MapHub<MessageHub>("/chat"); });

app.UseAuthentication();

Use this:

app.UseAuthentication();

app.UseSignalR(route => { route.MapHub<MessageHub>("/chat"); });
like image 56
Node.JS Avatar answered Sep 28 '22 01:09

Node.JS