fullstackproltd / AspNetCoreSpa

Asp.Net 7.0 & Angular 15 SPA Fullstack application with plenty of examples. Live demo:
https://aspnetcorespa.fullstackpro.co.uk
MIT License
1.48k stars 465 forks source link

SignalR authenticated user #226

Closed kmiskiewicz closed 6 years ago

kmiskiewicz commented 6 years ago

I hope you are well, I use your project as a base for my two personal projects and so far it works really great. It saved tons of time and It helped me to update my knowledge about the ASP CORE :-) Recently I started to use SignalR and I found an issue with hubs authentication.

In the Chat ( \AspNetCoreSpa.Web\SignalR\ChatHub.cs) I'd like to create hubs per user group but to achieve this I need information about the authenticated user. Unfortunately the user seems to be not authenticated despite the fact that in controllers the user is already authenticated. Here is my override OnConnectedAsync method: public override async Task OnConnectedAsync() { var user = _userManager.GetUserAsync(Context.User).Result; await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users"); await base.OnConnectedAsync(); } And here is a screenshot with user information problem

Could you take a look or give me a hint how to get an information about the current user in SignalR Hubs?

Best regards, Krzysztof

kmiskiewicz commented 6 years ago

I tried with this https://github.com/openiddict/openiddict-core/issues/467 unfortunately without success

asadsahi commented 6 years ago

@kmiskiewicz can you show me your angular code. Are you setting headers with bearer token for signal r to authenticate?

kmiskiewicz commented 6 years ago

Sure :-), I've just created a fresh fork from the project to make sure that the problem is not caused by my changes, here is the commit with the example. https://github.com/kmiskiewicz/AspNetCoreSpa/commit/8b5e3a5263bfd72f242ef6063dcec1b7331f5338 I'd like to have the logged user id in the OnConnectedAsync() method. I didn't change anything more

asadsahi commented 6 years ago

Here is is how it works:

1) add validation handler:

image

code:

 .AddOAuthValidation(options =>
               {
                   options.Events.OnRetrieveToken = context =>
                   {
                       context.Token = context.Request.Query["access_token"];

                       return Task.CompletedTask;
                   };
               })
  1. chat.component.ts
    
    import { Component, OnInit, Inject } from '@angular/core';
    import { HubConnection, HubConnectionBuilder } from '@aspnet/signalr';
    import { AccountService } from '../../core/services/account.service';

@Component({ selector: 'appc-chat', templateUrl: './chat.component.html', styleUrls: ['./chat.component.scss'] }) export class ChatComponent implements OnInit {

private _hubConnection: HubConnection; public async: any; message = ''; messages: string[] = [];

constructor(@Inject('BASE_URL') private baseUrl: string, private accountService: AccountService) { }

public sendMessage(): void { const data = Sent: ${this.message};

this._hubConnection.invoke('send', data);
this.messages.push(data);
this.message = '';

}

ngOnInit() {
    console.log(this.accountService.accessToken);
  this._hubConnection = new HubConnectionBuilder().withUrl(`${this.baseUrl}chathub?access_token=${this.accountService.accessToken}`).build();

this._hubConnection.on('send', (data: any) => {
  const received = `Received: ${data}`;
  this.messages.push(received);
});

this._hubConnection.start()
  .then(() => {
    console.log('Hub connection started');
  })
  .catch(err => {
    console.log('Error while establishing connection: ' + err);
  });

}

}


3. your ChatHub.cs code (which is exactly as you have got so far)

using System; using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR; using System.Collections.Generic; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Http; using System.Security.Claims; using Microsoft.AspNetCore.Authorization; using AspNetCoreSpa.Core.Entities;

namespace AspNetCoreSpa.Web.SignalR { public class Chat : Hub {

    UserManager<ApplicationUser> _userManager;

    public Chat(UserManager<ApplicationUser> userManager) : base()
    {
        _userManager = userManager;
    }
    public async Task Send(string message)
    {
        await Clients.All.SendAsync("Send", message);
    }

    public override async Task OnConnectedAsync()
    {
        var user = _userManager.GetUserAsync(Context.User).Result;

        await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");  //here instead of "SignalR Users" I'd like to use userId. 
        //Unfortunately the user is not authenticated (before I login in the application)
        await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception exception)
    {
        await Groups.RemoveFromGroupAsync(Context.ConnectionId, "SignalR Users");
        await base.OnDisconnectedAsync(exception);
    }

}

}

parys commented 6 years ago

@kmiskiewicz you should put Authorize attribute on hub. I faced with same issue, until you put this attribute, this code

 .AddOAuthValidation(options =>
               {
                   options.Events.OnRetrieveToken = context =>
                   {
                       context.Token = context.Request.Query["access_token"];

                       return Task.CompletedTask;
                   };
               })

don't call. but you user will get unathorized if not signed. Based on that I have two hubs, and switching them based on user signed|anonymous state.

asadsahi commented 6 years ago

@parys of course if purpose is to make signalr hub secure you can put Authorize attribute. But it isn't a requirement to get authenticated user. The code that I pasted above still gives you user information without decorating Authorize.

parys commented 6 years ago

@asadsahi I spent a lot of time, and maybe I did something wrong, but double checked all and without this attribute OnRetriveToken event don't appear, so user stayed anonymous

kmiskiewicz commented 6 years ago

I tested the code and everything works like a charm, thank you so much @asadsahi , @parys ! Great help as always 👍