feathersjs-ecosystem / authentication

[MOVED] Feathers local, token, and OAuth authentication over REST and Websockets using JSON Web Tokens (JWT) with PassportJS.
MIT License
317 stars 118 forks source link

user fails to signup with facebook if there is also local auth #168

Closed DenJohX closed 8 years ago

DenJohX commented 8 years ago

Im using a yeoman generated feathers app, with local and facebook authentication enabled.

The authentication service is as follows:

    const authentication = require('feathers-authentication');

    const FacebookStrategy = require('passport-facebook').Strategy;
    const FacebookTokenStrategy = require('passport-facebook-token');
    const LinkedinStrategy = require('passport-linkedin-oauth2').Strategy;
    const LinkedinTokenStrategy = require('passport-linkedin-token-oauth2').Strategy;

    module.exports = function() {
      const app = this;

      let config = app.get('auth');

      config.facebook.strategy = FacebookStrategy;
      config.facebook.tokenStrategy = FacebookTokenStrategy;
      config.linkedin.strategy = LinkedinStrategy;
      config.linkedin.tokenStrategy = LinkedinTokenStrategy;

      app.set('auth', config);
      app.configure(authentication(config));
    };

The user model:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

export const niveles = [
    {nombre: 'superadmin', nivel: 10},
    {nombre: 'owner', nivel: 3},
    {nombre: 'admin', nivel: 2},
    {nombre: 'user', nivel: 1}
];

const userSchema = new Schema({
  facebookId: { type: String },
  facebook: { type: Schema.Types.Mixed },
  linkedinId: { type: String },
  linkedin: { type: Schema.Types.Mixed },
  email: {type: String, required: true, unique: true},
  password: { type: String, required: true },
  nivel: { type: String, enum: niveles.map(n => n.nombre), required: true, default: 'owner'},

  createdAt: { type: Date, 'default': Date.now },
  updatedAt: { type: Date, 'default': Date.now }
});

const userModel = mongoose.model('user', userSchema);

export default userModel;

The local authentication works fine, but facebook does not, When the callback its called, the new user does not gets created because the validation fails with:

[BadRequest: user validation failed]
  type: 'FeathersError',
  name: 'BadRequest',
  code: 400,
  className: 'bad-request',
  data: undefined,
  errors: 
   { password: 
      { [ValidatorError: Path `password` is required.]
        message: 'Path `password` is required.',
        name: 'ValidatorError',
        properties: [Object],
        kind: 'required',
        path: 'password',
        value: undefined },
     email: 
      { [ValidatorError: Path `email` is required.]
        message: 'Path `email` is required.',
        name: 'ValidatorError',
        properties: [Object],
        kind: 'required',
        path: 'email',
        value: undefined } } }

The validation is there for new local user accounts, but should not work with facebook or others. Is there a way to configure this behavior? perhaps using the adquired email from facebook and a random generated password to create the new accounts.

ekryski commented 8 years ago

@DenJohX You either have to do your validation in hooks (which is what we prefer because it keeps you datastore agnostic) or write custom mongoose validations that are conditional. If you simply want to bring the email from the facebook profile top level then you always add a before hook to your user service create method.

We might make a change to the generator so that email and password aren't required by default but this is where the generator naturally starts to fall down. It's too hard to anticipate the requirements for every app so it'll probably have to be manual changes from here (ie. hooks or custom validators).

Changing you validations should allow you to continue on so I'm going to close this. If it doesn't work out then we can re-open and revisit. Thanks for reporting! đŸ˜„

DenJohX commented 8 years ago

Thanks for the guidance.

I'd ended up doing it all in a pre-save hook: obtaining the email from facebook and inserting it on the user, also generating a random password.

I'd leave the validation at model level, but yes, validation at service level seems to be the ideal.

Thanks again!