HNeukermans / ng2-signalr

angular2 - asp.net signalr library
MIT License
174 stars 81 forks source link

How to set Authorization Header ? #56

Open Usman235 opened 7 years ago

Usman235 commented 7 years ago

I am using ng2-signalrin ionic 2. Issue is that i don't know how to set authorization header. I have search but didn't find any example.

My code for connecting to server hub.

   let options: IConnectionOptions = { qs:{userId:1}, url: "http://192.168.0.211:44337"};

        console.log("Stage 1");
            //Header for 'this.singalR.connect'
        this.signalR.connect(options)
            .then((connection) => {                      

                console.log("Client Id: " + connection.id);                     
             }, (err) => {
                console.log("SignalR Error: " + JSON.stringify(err));
            });

How to set below header ?

  var headers = new Headers({
            'Content-Type': "application/json",
            "Authorization": 'Bearer ' + accessToken  //accessToken contain bearer value.
        });

Note: Before implementing Authorization same code working fine as i don't need to set authorization header.

HNeukermans commented 7 years ago

you need to send the token as a query parameter instead of qs:{userId:1} write qs:{userId:1, token: 'your token'}

Usman235 commented 7 years ago

@HNeukermans Thanks for response.

Issue it that is have two signalr client , one is in simple angularjs and other is ionic 2 .

In Simple Angularjs i'm doing following to set the header. (Which is working).

$.signalR.ajaxDefaults.headers = new Headers({
            'Content-Type': "application/json",
            "Authorization": 'Bearer ' + accessToken  //accessToken contain bearer value.
        });

I'm wondering if there is way to send headers just like above . If i go for query string option that is what you suggested then i have to change a lot of code on server side as well as on simple angularjs signalr client .

Is there any other way ?

HNeukermans commented 7 years ago

Hi @Usman235,,

thx a ton. I didn't even know this existed inside signalr. Sorry, my fault... Seems like a next ng2-signalr super feature !! As a workaround inside your typescript project you can always do

declare var $: any;
$.signalR.ajaxDefaults.headers = new Headers({
            'Content-Type': "application/json",
            "Authorization": 'Bearer ' + accessToken  //accessToken contain bearer value.
        }); 

If you feel comfortable writing typescript,.. Would you like to try to add the feature to ng2-signalr yourself ?

From my part, I will add this feature, the moment I find some free time...

Maybe a small question to you about the ajaxDefaults? The headers you add there: Are these send with every signalr request you do? If you change the ajaxDefaults headers when you already have an ongong connection,are the changes taken into account ? Or do you need to create a newly instantiated connection for that ?

Usman235 commented 7 years ago

@HNeukermans sorry for late response.

I'm beginner to typescript . :)

The headers you add there: Are these send with every signalr request you do?

Yes headers are send with every signalr request.

If you change the ajaxDefaults headers when you already have an ongong connection,are the changes taken into account ? Or do you need to create a newly instantiated connection for that ?

Yes if i change ajaxDefaults headers changes are automatically applied i don't need to newly instantiated connection.

Unfortunately below didn't work for me.

declare var $: any;
$.signalR.ajaxDefaults.headers = new Headers({
            'Content-Type': "application/json",
            "Authorization": 'Bearer ' + accessToken  //accessToken contain bearer value.
        });

As i have to meet deadline so i implemented qs method which you suggested me . And at server side added a new middlerware which check for every request if token is in query string simple it just add that token in request header.

Jrubzjeknf commented 7 years ago

Found a solution. You need to use the window object to get access to jQuery, you just have to cast it to an any.

    let $ = (<any>window).$;
    $.signalR.ajaxDefaults.headers = {
      'Content-Type': "application/json",
      "Authorization": 'Bearer ' + this.oauthService.getAccessToken()
    };
mcgri commented 6 years ago

How does the real-life implementation look like? I having issues with injecting my oauthService there... Are you putting it into the createConfig(): SignalRConfiguration ?

Jrubzjeknf commented 6 years ago

We created a custom service where the SignalR and OAuthService are injected. Below is a basic example where the ISignalRConnection is provided through an Observable. This way, if the connection is interrupted and re-establised, you are able to automatically resubscribe to the new connection.

import { Injectable, OnInit } from '@angular/core';
import { SignalR, ISignalRConnection } from 'ng2-signalr';
import { Notice } from "app/feed/notice/notice.model";
import { Observable } from "rxjs/Observable";
import { Subject } from "rxjs/Subject";
import { OAuthService } from "angular-oauth2-oidc/dist";

@Injectable()
export class SignalrService {
  private subject: Subject<ISignalRConnection>;
  public connection: Observable<ISignalRConnection>;

  constructor(private signalR: SignalR, private oauthService: OAuthService) {
    this.subject = new Subject<ISignalRConnection>();
    this.connection = this.subject.asObservable().shareReplay(1);

    if (navigator.onLine) {
      this.connect();
    }
  }

  private connect() {
    // Set auth headers.
    let $ = (<any>window).$;
    $.signalR.ajaxDefaults.headers = {
      'Content-Type': "application/json",
      "Authorization": 'Bearer ' + this.oauthService.getAccessToken()
    };

    let promise = this.signalR.connect();

    promise
      .then(x => {
        this.subject.next(x);

        x.errors.subscribe(y => {
          if (y.message.includes(`Couldn't reconnect within the configured timeout`)) {
            this.reconnect();
          }
        })
      })
      .catch(x => this.reconnect());
  }

  private reconnect() {
    let ms = 3000;
    console.log(`Manual reconnect after ${ms}ms`);
    setTimeout(() => this.connect(), ms)
  }
}

And to listen for a message, inject this SignalrService and subscribe to the connection Observable:

    this.signalrService
        .connection
        .subscribe(connection => connection
            .listenFor('your-message')
            .subscribe((yourMessage: IYourMessage) => {
                // do something
            }));
IbrahimElkhatib commented 2 years ago

from jquery.signalR.core.js: // With sse or ws, access_token in request header is not supported if (connection.transport && connection.accessToken) { if (connection.transport.name === "serverSentEvents" || connection.transport.name === "webSockets") { url += "&access_token=" + window.encodeURIComponent(connection.accessToken); } }

rely on $.signalR.ajaxDefaults.headers will only enable longPolling, but sse or ws negotiate request will return 401, I think relying on query params is better.

Prefix1802 commented 1 year ago

Hello,

Is it already possible to send an Authorization header something like this:

(headers: request.headers.set('Authorization', 'Bearer ' + token) <-- using a JWT token here.

instead of via query string parameters?

In my angular app i use an interceptor where i add a header to all my http requests. I did not test it yet but maybe if i try it with an interceptor it could work? this week i will try that and let you know... fingers crossed :)

Edit: Maybe this will help if we are bound to query string parameters. -> https://stackoverflow.com/a/57460785