1
votes

I have an app. When i am trying to login with facebook it is throwing error.

Error {"error":"E_VALIDATION","status":400,"summary":"1 attribute is invalid","model":"User","invalidAttributes":{"email":[{"rule":"string","message":"undefined should be a string (instead of \"null\", which is a object)"},{"rule":"email","message":"\"email\" validation rule failed for input: null"},{"rule":"required","message":"\"required\" validation rule failed for input: null"}]}} [TypeError: Cannot read property 'nickname' of undefined]

My user model looks like this:

/**
* User
*
* @module      :: Model
* @description :: A short summary of how this model works and what it          represents.
* @docs     :: http://sailsjs.org/#!documentation/models
*/

var Q=require('q')
module.exports = {

/* e.g.
nickname: 'string'
*/
attributes  : {
  provider: {
      type:'string',
      required:true
  },
  uid: {
      type:'integer',
      required:true
  },
  name: 'string',
  email: {
      type:'string',
      email:true,
      required:true
  },
  firstname: {
      type:'string'
  },
  lastname: {
      type:'string'
  },
  admin:{
      type:'boolean',
      defaultsTo:false
  },
  lastLoginAt:{
      type:'date'
  },
  firstLoggedInAt:{
      type:'date'
  },
  profileUpdated:{
      type:'boolean',
      defaultsTo:false
  },
  phoneNumber:{
      type:'integer'
  },
  dealClaims:{
      collection:'dealClaim',
      via:'user'
  },
  parties:{
      collection:'party',
      via:'user'
  },
  origin:{
      type:'string',
      defaultsTo:'web'
  },
  city:{
      type:'string',
      defaultsTo:'Bangalore'
  },
  currentCity:function(){
     return this.city||'Bangalore'
  },
  claimDealWith:function(dealId){
      var deferredClaim= Q.defer()
      sails.log.info('Im here',this.email,dealId,this.phoneNumber);
      Dealclaim.create({email:this.email,phoneNumber:this.phoneNumber,deal:dealId,user:this.id},function(err,dealClaim){

          if(err||!dealClaim){
               deferredClaim.reject(err)
               return
           }
          sails.log.info('dealClaim',dealClaim)
          Deal.findOne(dealClaim.deal)
              .populate('listing')
              .then(function(deal){
                  deferredClaim.resolve({deal:deal,dealClaim:dealClaim})
              })
              .fail(function(err){
                  deferredClaim.reject(err)
              })

      })
      return deferredClaim.promise
  },
  login:function(){
      var deferred= Q.defer()
      var currentDate=new Date()
      var updateConditions={lastLoginAt:currentDate}
      if(!this.firstLoggedInAt){
          updateConditions.firstLoggedInAt=currentDate
      }
      User.update(this.id,updateConditions,function (err){
          sails.log.info('last login date for user:',this.id)
          if(err){
              deferred.reject(err)
          }
          deferred.resolve()
      })
      return deferred.promise
  },
  completeProfile:function(data){
      var deferred= Q.defer()
      this.phoneNumber=(data.phoneNumber||this.phoneNumber);
      this.email=(data.email||this.email)
      this.city=(data.city||this.city)
      sails.log.info('city==>',this.city)
      //sails.log.info('email',this.email,User.validateEmail(this.email),(this.email==='[email protected]'||(!this.email)||User.validateEmail(this.email)),'phone',this.phoneNumber)
      if(this.email==='[email protected]'||(!this.email)||!User.validateEmail(this.email)){
          deferred.reject({ValidationError:{invalidEmail:'Invalid Email'}})
          return deferred.promise
      }
      if((!this.phoneNumber)||isNaN(this.phoneNumber)){
          deferred.reject({ValidationError:{invalidPhoneNumber:'Invalid Phone number'}})
          return deferred.promise
      }
      this.profileUpdated=true
      this.save(function(err){
          if(err){
              sails.log.error(err.stack)
              deferred.reject(err)
          }else{
              deferred.resolve()
          }
      })
      return deferred.promise
  },
  providedEmail:function(){
       return this.email==='[email protected]'?undefined:this.email;
  },
  planMyParty:function(party){
      var deferred= Q.defer()
      //i should us
      this.parties.add({numberOfPeople:party.numberOfPeople,locationOrPlace:party.locationOrPlace})
      this.save(function(err){
          if(err){
            deferred.reject(err)
            return
          }
          deferred.resolve(party)
      })
      return deferred.promise
  }


  },
  validateEmail:function(email){
  var regex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
  return regex.test(email)
  },
      beforeCreate:function makeAdmin(values,next){
  sails.log.warn("BOOYA IM HERE!!")

  var deferred=Q.defer()
  Admin.findOneByEmail(values.email).then(function foundAdmin(err,admin){
          if(err || !admin){
              sails.log.warn("User with email"+values.email+" tried to gain access to restricted urls")
              //return next({err:["only admins allowed"]},null)
              values.admin=false
          } else{
              values.admin=true
          }
        deferred.resolve()
      })
  deferred.promise.fin(function(){
      next()
  })
  return deferred.promise

  },
    createSession:function(profile){
    var deferred= Q.defer();
    if(!profile){
        deferred.reject(new Error("Unable to login to the server"))
        return deferred.promise
    }
    User.findOne({
            or: [
                {uid: parseInt(profile.id)},
                {uid: profile.id}
            ]
        }
    ).exec(function (err, user) {
            if (user) {
                //sails.log.info('User Exists')
                user.login()
                    .then(function(){
                        deferred.resolve(user);
                    }).fail(function(){
                        deferred.reject();
                    })
            } else {

                var data = {
                    provider: profile.provider,
                    uid: profile.id,
                    name: profile.displayName
                };

                if (profile.emails && profile.emails[0] && profile.emails[0].value) {
                    data.email = profile.emails[0].value;
                }
                if (profile.name && profile.name.givenName) {
                    data.firstname = profile.name.givenName;
                }
                if (profile.name && profile.name.familyName) {
                    data.lastname = profile.name.familyName;
                }
                if(!profile.email){
                    data.email='[email protected]'
                }
                data.origin=profile.origin
                User.create(data).exec(function (err, user) {
                    if(err||!user){
                        sails.log.error('Unable to create user',err.stack)

                        deferred.reject(err)
                    }
                    if(user){
                        user.login()
                            .then(function(){
                                deferred.resolve(user);
                            }).fail(function(){
                                deferred.reject();
                            })
                    }
                });
            }
        });
    return deferred.promise;
},
findForClaim:function(id){
    var deferred= Q.defer();
    User.findOne(id)
        .exec(function(err,user){
            if(err){
               deferred.reject(err)
                return
            }
            if((!user.email)||(!user.phoneNumber)){
                deferred.reject('No email or phoneNumber')
                return
            }
            sails.log.info('Here-->',user.email)
            deferred.resolve(user)
        })
    return deferred.promise
}




};

AuthController code looks like this:

facebook: function (req, res) {
    passport.authenticate('facebook', { failureRedirect: '/login', scope: ['public_profile','email'] },
        function (err, user) {
            req.logIn(user, function (err) {
                if (err) {
                    console.log(err);
                    res.view('500');
                    return;
                }

                res.redirect('/');
                return;
            });
        })(req, res);
},

facebook config file looks like this:

facebook:{
        clientID: "ID",
        clientSecret: "SECRET",
        callbackURL: "http://localhost:1337/auth/facebook/callback"
    }

View page looks like this:

<div class="main">
<% if(flash && flash.err) { %>
<ul class="alert alert-danger">
    <% Object.keys(flash.err).forEach(function(index){%>
    <% if (flash.err[index].message != undefined) {%>
    <li><%=JSON.stringify(flash.err[index].message)%></li>
    <% } %>
    <% })%>
    <li></li>
</ul>
<% }%>
<h1 id="main-title">Please sign in</h1>
<a href="/auth/facebook/" class="btn">with Facebook</a>
<a href="/logout" class="btn">Logout</a>

When i click on "With facebook" button m getting logs like this:

info: Device type { type: 'desktop' }
verbose: Running res.view([object Object])...
verbose: Using layout:  /home/testgulp/Desktop/funtestgulp/views/layoutBW
verbose: Rendering view :: site/index (located @ /home/testgulp/Desktop/funtestgulp/views/site/index)
info: inside flash policy
verbose: Running res.view()...
verbose: Using layout:  /home/testgulp/Desktop/funtestgulp/views/layoutBW
verbose: Rendering view :: auth/index (located @  /home/testgulp/Desktop/funtestgulp/views/auth/index)
info: inside flash policy
info: inside flash policy
info: Error {"error":"E_VALIDATION","status":400,"summary":"1 attribute is invalid","model":"User","invalidAttributes":{"email":[{"rule":"string","message":"`undefined` should be a string (instead of \"null\", which is a object)"},{"rule":"email","message":"\"email\" validation rule failed for input: null"},{"rule":"required","message":"\"required\" validation rule failed for input: null"}]}}
[TypeError: Cannot read property 'nickname' of undefined]
verbose: Running res.view(500)...
verbose: Using layout:  /home/testhulp/Desktop/funtestgulp/views/layoutBW
verbose: Rendering view :: 500 (located @ /home/testgulp/Desktop/funtestgulp/views/500)
1
Can you post your User model code so we can see what's wrong? It looks like the validation rule for email is wrong. Sails changed the API for this between 0.10 and 0.11 - coagmano
posted the user model. Thanx for the response. - Vaibhav Kumar Ambeshta
Doesn't look like email validation problem. It looks like your app isn't passing an email for facebook signups which then fails the required rule (and the rest as well) - coagmano
Can you please suggest a solution? - Vaibhav Kumar Ambeshta
Sorry, there's not enough info here to suggest a solution. Is it possible to log what is being passed to Sails for auth? What else have you tried so far? How have you implemented passport? - coagmano

1 Answers

0
votes

I have this in my config/passport.js:

facebook: {
    name: 'Facebook',
    protocol: 'oauth2',
    strategy: require('passport-facebook').Strategy,
    options: {
     clientID: 'your-client-id',
     clientSecret: 'your-client-secret',
     scope: ['email'] /* email is necessary for login behavior */
}
},

I tested it some time ago and it worked fine. You probably need to complete the faceboook configuration, specifically you probably have to set an scope in the config file.

I followed this website as reference:

https://www.npmjs.com/package/sails-generate-auth

In addition, you must register your app in the facebook developer site:

https://developers.facebook.com/