Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SignalR + Angular: how to add Bearer token to Http Headers

I made an asp.net core 2.0 SignalR Hub which uses Bearer Token for Authentication. Now I'm a bit lost on how to connect to it via the SignalR Angular 5 client. I actually can connect if I remove authorization from the Hub, so the connection is working, now I believe I just need to add the Authorization Bearer to the Http Headers of the connection.

The SignalR client reference in the package.json file of my Angular 5 project: "@aspnet/signalr-client": "^1.0.0-alpha2-final"

My Angular component:

import { Component, OnInit } from '@angular/core';
import { finalize } from 'rxjs/operators';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { ToastrService } from 'ngx-toastr';
import { AuthenticationService } from '../core/authentication/authentication.service';
import { HubConnection } from '@aspnet/signalr-client';

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

  quote: string;
  isLoading: boolean;
  jwtToken:string;
  private hubConnection: HubConnection;

  constructor(
    private _http: HttpClient, 
    private _auth : AuthenticationService,
    private _toastr: ToastrService) { }

  ngOnInit() {
    this.isLoading = false;
    this.jwtToken = this._auth.currentToken;

    this.hubConnection = new HubConnection('http://localhost:27081/hub/notification/');
    this.hubConnection
      .start()
      .then(() => console.log('Connection started!'))
     .catch(err => console.error('Error while establishing connection :(', err));        
    this.hubConnection.on("send", data => {
        console.log(data);
    });
  }

  showToastr(){
    this._toastr.success('Hello world!', 'Toastr fun!');
  }

}

Due to reading similar questions I tried: this.hubConnection.Headers.Add("token", tokenValue); but it doesn't work, the Headers property doesn't exist.

How can I add the Bearer token to the Http Headers of the HubConnection?

Thanks for any help

like image 541
André Luiz Avatar asked Jan 05 '18 18:01

André Luiz


People also ask

How do I add a header in SignalR?

You can add headers on either Connection or HubConnection using the . NET client (as @abnanda mentions): var connection = new Connection("http://signalr/"); connection. Headers.

Does SignalR use HTTP?

SignalR uses Web Sockets and the HTML5 API that helps in bidirectional communication. It also provides an API for server-to-client Remote Procedure Calls (RPC) call, it may be something new for you because most of the time we use a request and response model.

Does SignalR need SSL?

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

What is bearer token in angular?

This type of token is known as a Bearer Token, meaning that it identifies the user that owns it, and defines a user session. A bearer token is a signed temporary replacement for the username/password combination!


3 Answers

To do this with @aspnet/signalr (^1.1.4) you can use the following code

const options: IHttpConnectionOptions = {
  accessTokenFactory: () => {
    return "Token is resolved here";
  }
};

const connection = new signalR.HubConnectionBuilder()
  .configureLogging(signalR.LogLevel.Information)
  .withUrl(`${environment.apiUrl}/notify`, options)
  .build();

Also add an annotation to your Hub

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

As a side note, SignalR when using the websocket protocol does not seem to attach the Bearer token as a header and instead adds it to the request URL as an 'access_token' parameter, this requires you to configure your authentication to handle this token when signalR chooses to use ws.

services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(x =>
            {
                x.RequireHttpsMetadata = false;
                x.SaveToken = true;
                x.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ValidateIssuer = false,
                    ValidateAudience = false
                };
                x.Events= new JwtBearerEvents
                {
                    OnMessageReceived = context =>
                    {
                        var accessToken = context.Request.Query["access_token"];

                        // If the request is for our hub...
                        var path = context.HttpContext.Request.Path;
                        if (!string.IsNullOrEmpty(accessToken) && (path.StartsWithSegments("/notify")))
                        {
                            // Read the token out of the query string
                            context.Token = accessToken;
                        }
                        return Task.CompletedTask;
                    }
                };
            });
like image 159
Darryn Hosking Avatar answered Oct 24 '22 03:10

Darryn Hosking


From reading their source code and tests, it looks like you can provide an options object containing your access token, like so

var options = {
    transport: transportType,
    logging: signalR.LogLevel.Trace,
    accessToken: function () {
        return jwtToken;
    }
};

hubConnection = new signalR.HubConnection('/authorizedhub', options);
hubConnection.start();

The code in particular in the test file here

like image 42
user184994 Avatar answered Oct 24 '22 02:10

user184994


I couldn't find a way to solve using angular but I did it using asp.net following this article.

This is what I did: Now to connect I pass the jwt token in the querystring and specify transport type:

const options = {
      transport: TransportType.WebSockets
};
this.hubConnection = new HubConnection('http://localhost:27081/hub/notification/?token='+this.jwtToken, options);

And then in the startup.cs > ConfigureServices() :

        services
            .AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(cfg =>
            {
                cfg.RequireHttpsMetadata = false;
                cfg.SaveToken = true;
                cfg.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidIssuer = Configuration["JwtIssuer"],
                    ValidAudience = Configuration["JwtIssuer"],
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtKey"])),
                    ClockSkew = TimeSpan.Zero // remove delay of token when expire
                };
                cfg.Events = new JwtBearerEvents
                {
                    OnMessageReceived = context =>
                    {
                        if (context.Request.Query.TryGetValue("token", out StringValues token)
                        )
                        {
                            context.Token = token;
                        }

                        return Task.CompletedTask;
                    },
                    OnAuthenticationFailed = context =>
                    {
                        var te = context.Exception;
                        return Task.CompletedTask;
                    }
                };
            });
like image 36
André Luiz Avatar answered Oct 24 '22 02:10

André Luiz