Skip to content
This repository has been archived by the owner on Mar 22, 2022. It is now read-only.

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

Closed
zender opened this issue Aug 19, 2016 · 15 comments
Closed

Comments

@zender
Copy link

zender commented 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 zender changed the title Autentication problem with socketIO Authentication problem with socketIO Aug 19, 2016
@zender
Copy link
Author

zender commented 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
Copy link
Member

daffl commented 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
Copy link
Author

zender commented 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
Copy link
Member

daffl commented Aug 20, 2016

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

@zender
Copy link
Author

zender commented 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
Copy link
Contributor

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
Copy link
Contributor

bertho-zero commented 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
Copy link
Author

zender commented Aug 29, 2016

any updates???

@fedevela
Copy link

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

@fedevela
Copy link

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
Copy link
Member

marshallswain commented 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.

marshallswain added a commit to feathersjs/docs that referenced this issue Sep 24, 2016
You have to authenticate after the transport upgrades.  See feathersjs-ecosystem/authentication#275
@ekryski
Copy link
Member

ekryski commented Oct 1, 2016

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

@marshallswain
Copy link
Member

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

@ekryski
Copy link
Member

ekryski commented Oct 4, 2016

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

@ekryski ekryski changed the title Authentication problem with socketIO Client should ensure socket.io upgrade is complete before authenticating Oct 4, 2016
@marshallswain
Copy link
Member

Issue moved to feathersjs/feathers-authentication-client #4 via ZenHub

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

6 participants