Search code examples
.netjwt.net-6.0bearer-tokenasp.net-core-signalr

Jwt Token arrives as Null SignalR


I'm working in a Notifications Service using SignalR in .net 6.0. I need to get data user through a token by query, however, it arrives like null. I don't find more information and I don't know what else to do.

My Program.cs:

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
    o.TokenValidationParameters = new TokenValidationParameters
    {
        //ValidIssuer = builder.Configuration["Jwt:Issuer"],
        // ValidAudience = builder.Configuration["Jwt:Audience"],

        IssuerSigningKey = new SymmetricSecurityKey
            (Encoding.UTF8.GetBytes(builder.Configuration["secreatKey"])),
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = false,
        ValidateIssuerSigningKey = true
    };

    o.Events = new JwtBearerEvents
    {
        OnMessageReceived = context =>
        {
            var accessToken = context.Request.Query["access_token"];
            if (string.IsNullOrEmpty(accessToken) == false)
            {
                context.Token = accessToken;
            }

            return Task.CompletedTask;
        }
    };
});

My Hub:

public class NotificationHub : Hub<INotificationHub>
{
    //Crea la Conexion

    private readonly NotificationMemoryStorage _notificationStorage;

    public NotificationHub(NotificationMemoryStorage notificationMemoryStorage)
    {
        _notificationStorage = notificationMemoryStorage;
    }

    public override async Task OnConnectedAsync()
    {
        
        var userId = GetUserIdFromToken();
        if(!string.IsNullOrEmpty(userId))
        {
            var notifications = _notificationStorage.GetNotifications(userId);
            foreach (var notification in notifications)
            {
                await Clients.User(userId).SendNotification(notification);
            }
            _notificationStorage.ClearNotifications(userId);
            var response = new ServerResponse
            {
                status = "Acepted",
            };
            await Clients.Client(Context.ConnectionId).ConnectionResponse(response);

        }
            await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception? exception)
    {
        await base.OnDisconnectedAsync(exception);
    }

    public async Task MessageCheck(NotificationRequestModel request)
    {
        //meter validaciones
        switch (request.Target.Device)
        {
            case "Admin":
                if (request.Target.User.Contains("*"))
                {
                    SendNotificationToAllUsers(request.Notification);
                }
                else
                {
                    //SendNotificationToSpecificUser(request, request.Target.User);
                }
                break;
        }
    }

    //Desconecta del Servidor
    public async Task SendNotificationToAllUsers(NotificationModel message)
    {
        await Clients.All.SendNotificationToAllUsers(message);
    }
    private string GetUserIdFromToken()
    {
        var token = Context.GetHttpContext();
        var jwt = token.Request.Headers["access_token"];

        var identity = (ClaimsIdentity)Context.User.Identity;
        var userIdClaim = identity.FindFirst(ClaimTypes.NameIdentifier);
        return userIdClaim?.Value;
    }

    //public async Task SendNotificationToSpecificUser(NotificationRequestModel message, List<string> target)
    //{
    //    foreach (var item in target)
    //    {
    //        //if (ConnectedUsers.TryGetValue(item, out var user))
    //        //{
    //        await Clients.Client(user.ConnectionId).SendNotification(message);
    //        //}
    //    }
    //}
}

In here the data arrives as null:

private string GetUserIdFromToken()`enter code here`
        {
            var token = Context.GetHttpContext();
            var jwt = token.Request.Headers["access_token"];
    
            var identity = (ClaimsIdentity)Context.User.Identity;
            var userIdClaim = identity.FindFirst(ClaimTypes.NameIdentifier);
            return userIdClaim?.Value;
        }

Attach Image: enter image description here

I recognize I don't know what I'm doing.


Solution

  • Some minor issues along the way, try it like this

    Step 1:

    Remove bearer-prefix from the querystring request

    ?access_token=bearerXXXX --> ?access_token=XXXX

    Step 2:

    Read the token from the query and apply the token and a bearer-prefix with a space to the authorization header in the OnMessageReceived event

    OnMessageReceived = context =>
    {
        var accessToken = context.Request.Query["access_token"];
        if (!string.IsNullOrEmpty(accessToken))
        {
            context.Request.Headers.Add("Authorization", $"Bearer {token}");
        }
    
        return Task.CompletedTask;
    }
    

    Step 3:

    Enforce the authorization on your hub by adding the Authorize-attribute to the class

    [Authorize]
    public class NotificationHub : Hub<INotificationHub>