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
You can add headers on either Connection or HubConnection using the . NET client (as @abnanda mentions): var connection = new Connection("http://signalr/"); connection. Headers.
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.
If your SignalR application transmits sensitive information between the client and server, use SSL for the transport.
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!
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;
}
};
});
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
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;
}
};
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With