scydev
scydev

Reputation: 31

How to prevent multiple connection from one JWT claim to one hub [SignalR, C#]

Let's say our application accepts JWT Bearer authorization on SignalR Hub.

Since we have a ban on anonymous connections, then we also have data that can be limited to this, right? The task is to block several connections at the same time from this token to the Hub.

I came up with the idea to make a list of users and add / remove via OnConnected / OnDisconnected, and just reject requests, is this a good idea or are there better options?

Upvotes: 1

Views: 295

Answers (1)

scydev
scydev

Reputation: 31

If anyone insterested in my implementation, here it is (i think it can be better):

Hub.cs:

    private static readonly ConnectionMapping Connections = new();
    
    public async Task JoinGroup(string group)
    {
        string username = Context.User.Identity.Name;

        Connections.Add(group, username);
        
        await Groups.AddToGroupAsync(Context.ConnectionId, group);
        await Clients.Group(group).SendAsync("UserConnected", Connections.GetConnections(group));
    }

    public override async Task OnDisconnectedAsync(Exception? exception)
    {
        string username = Context.User.Identity.Name;
        string group = Connections.GetKey(username);
        
        Connections.Remove(group, username);
        
        await Clients.Group(group).SendAsync("UserConnected", Connections.GetConnections(group));
        
        await base.OnDisconnectedAsync(exception);
    }

ConnectionMapping.cs:

public class ConnectionMapping
{
    private readonly Dictionary<string, List<string>> _groups = new();

    public void Add(string key, string connectionId)
    {
        List<string> connections;
        
        if (!_groups.TryGetValue(key, out connections))
        {
            connections = new List<string>();
            _groups.Add(key, connections);
        }

        connections.Add(connectionId);
    }

    public void Remove(string key, string connectionId)
    {
        List<string> connections;
        
        if (!_groups.TryGetValue(key, out connections))
            return;

        connections.Remove(connectionId);

        if (connections.Count == 0)
            _groups.Remove(key);
    }
    
    public IEnumerable<string> GetConnections(string key)
    {
        List<string> connections;
        
        if (_groups.TryGetValue(key, out connections))
            return connections.Distinct();

        return Enumerable.Empty<string>();
    }

    public string GetKey(string connectionId)
    {
        string key = _groups.FirstOrDefault(x => x.Value.Contains(connectionId)).Key;

        if (!key.IsNullOrEmpty()) 
            return key;
        
        return string.Empty;
    }
}

Upvotes: 1

Related Questions