Skip to content
This repository has been archived by the owner on Apr 23, 2019. It is now read-only.

Client should ensure socket.io upgrade is complete before authenticating #4

Closed
marshallswain opened this issue Oct 25, 2016 · 1 comment

Comments

@marshallswain
Copy link
Member

@zender commented on Fri Aug 19 2016

I have the following code in Angular 2:

import { Injectable } from '@angular/core';
import {Parameters} from '../../../parameters';
import {AuthService} from '../security/auth.service';

declare const io;
declare const feathers;

@Injectable()
export class SocketService {

  private _app: any;

  constructor(private authService: AuthService) {
    this.connect();
    this.authenticate();
  }

  connect(): void {
    var authService: AuthService = this.authService;
    var socket: any = io(Parameters.URL);

    var app: any = feathers()
      .configure(feathers.socketio(socket))
      .configure(feathers.hooks())
      .configure(feathers.authentication({ storage: localStorage, tokenKey: 'jwt'}))
    ;

    this._app = app;
  }

  authenticate(): Promise<any> {
    return this._app.authenticate({
      type: 'token',
      token: this.authService.getToken(),
    }).then(() => {
      console.info('Authenticated!');

    }).catch(function(error){
      console.info('Error authenticating!', error);
    });
  }

  getResource(service: string): any {
    return this._app.service(service);
  }
}

authenticate() method works fine but when I try to call getResource("some service") .someMethod then it says that token is missing.

Any suggestion how to fix that?


@zender commented on Fri Aug 19 2016

Also the code below seems not to work (params are never set):

app.mixins.push(function(service) {
  service.before((hook) => {
    Object.assign(hook.params, {
      user: authService.getUser(),
      token:authService.getToken()
    });

    return hook;
  });
});

@daffl commented on Fri Aug 19 2016

I'm not sure what getResource("some service") does. The token will only be set automatically on service calls that are used on the client via app.service('some service'). For any other client mechanism you will have to add it yourself.


@zender commented on Fri Aug 19 2016

It is working in REST as expected but using socketIO, token is not sent to the server at all.

Shall I provide you with some code?


@daffl commented on Sat Aug 20 2016

Yes please. Maybe a repository to reproduce. I have definitely used authentication and service requests with Socket.io before.


@zender commented on Sun Aug 21 2016

I have created a simple featherjs app: https://github.com/zender/feathers-285
Also client app (angular2): https://github.com/zender/feathers-client-285

REST provider is working without any problems but socketio is not sending the token.

feathers-client does not read token from local storage (I had to set it manually) - https://github.com/zender/feathers-client-285/blob/master/src/app/shared/services/rest.service.ts#L36

Also hooks are not working either on the client side: https://github.com/zender/feathers-client-285/blob/master/src/app/shared/services/rest.service.ts#L25

Thank you in advance for your support.


@bertho-zero commented on Thu Aug 25 2016

I have the same problem, I am logged in rest but not in socket, I get an error 'token missing'.

So it would be nice to reconnect the user to the socket with cookies.


@bertho-zero commented on Sun Aug 28 2016

I sign in with facebook JS SDK in react app, after I use facebook-token passport strategy, it's work but if I also use a socket connection for a chat I'm not connected, how to connect the socket after a rest connection?

On client-side I noticed that we can't connect with socket on social provider: app.authenticate('facebook', data) is disallow and app.service('/auth/facebook').create() work but only if app use rest.


@zender commented on Mon Aug 29 2016

any updates???


@fedevela commented on Mon Aug 29 2016

I would recommend using a basic feathers example that works for you, then modifying what you need on top of that.


@fedevela commented on Mon Aug 29 2016

When i deploy a feathers application, the token gets set automatically when I access through the feather services, but get a 401 when doing it any other way.


@marshallswain commented on Sat Sep 24 2016

For anybody having this issue, please make sure that your socket.io connection's upgrade process has fully stabilized before you call authenticate.

Socket.IO never assumes that WebSocket will just work, because in practice there’s a good chance that it won’t. Instead, it establishes a connection with XHR or JSONP right away, and then attempts to upgrade the connection to WebSocket. Compared to the fallback method which relies on timeouts, this means that none of your users will have a degraded experience. http://socket.io/blog/introducing-socket-io-1-0/

If you call authenticate right when socket.io connects, and you haven't locked the transports to websocket, the XHR-based connection with be authenticated, but then will subsequently be lost in the upgrade to JSONP or WebSockets. There are two workarounds to this problem. One is to configure your socket.io connection transports to only use WebSockets, then you can always assume you've got the right connection. The client code would look something like this:

const feathers = require('feathers/client')
const socketio = require('feathers-socketio/client');
const hooks = require('feathers-hooks');
const io = require('socket.io-client');
const authentication = require('feathers-authentication/client');

const socket = io('http://api.my-feathers-server.com', {
  transports: ['websocket']
});
const app = feathers()
  .configure(hooks())
  .configure(socketio(socket));
  .configure(authentication({ storage: window.localStorage }));

app.authenticate();

The downside to the above is that it won't work with clients that aren't compatible with WebSockets. So, the alternative would be to make sure you authenticate after the transport upgrade has occurred:

const feathers = require('feathers/client')
const socketio = require('feathers-socketio/client');
const hooks = require('feathers-hooks');
const io = require('socket.io-client');
const authentication = require('feathers-authentication/client');

const socket = io('http://api.my-feathers-server.com', {
  transports: ['websocket']
});
const app = feathers()
  .configure(hooks())
  .configure(socketio(socket));
  .configure(authentication({ storage: window.localStorage }));

socket.io.engine.on('upgrade', function(transport) {
  console.log('transport changed');
  app.authenticate();
});

See this Stack Overflow answer for more examples.


@ekryski commented on Fri Sep 30 2016

@marshallswain @daffl maybe we should do this inside the client itself.


@marshallswain commented on Sat Oct 01 2016

I think so. It's something that you would want in every app, I think.


@ekryski commented on Tue Oct 04 2016

Ya I agree. Going to change the title on this.

@ekryski
Copy link
Member

ekryski commented Nov 22, 2016

This is now complete.

@ekryski ekryski closed this as completed Nov 22, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants