将Google帐户与使用Parse.com中的电子邮件创建的现有帐户相关联

时间:2016-01-04 16:36:07

标签: javascript parse-platform login cloud-code google-login

我已经在解析中实现了谷歌登录。这是我的代码:

var querystring = require('querystring');
var _ = require('underscore');
var Buffer = require('buffer').Buffer;

var googleValidateEndpoint = 'https://www.googleapis.com/oauth2/v1/userinfo';
var TokenStorage = Parse.Object.extend("TokenStorage");

var restrictedAcl = new Parse.ACL();
restrictedAcl.setPublicReadAccess(false);
restrictedAcl.setPublicWriteAccess(false);

Parse.Cloud.define('accessGoogleUser', function(req, res) {
  var data = req.params;
  var token = data.code;
  /**
   * Validate that code and state have been passed in as query parameters.
   * Render an error page if this is invalid.
   */
  if (!(data && data.code)) {
    res.error('Invalid auth response received.');
    return;
  }
  Parse.Cloud.useMasterKey();
  Parse.Promise.as().then(function() {
    // Validate & Exchange the code parameter for an access token from Google
    return getGoogleAccessToken(data.code);
  }).then(function(httpResponse) {
    var userData = httpResponse.data;
    if (userData && userData.id) {
      return upsertGoogleUser(token, userData, data.email);
    } else {
      return Parse.Promise.error("Unable to parse Google data");
    }
  }).then(function(user) {
    /**
     * Send back the session token in the response to be used with 'become/becomeInBackground' functions
     */
    res.success(user.getSessionToken());
  }, function(error) {
    /**
     * If the error is an object error (e.g. from a Parse function) convert it
     *   to a string for display to the user.
     */
    if (error && error.code && error.error) {
      error = error.code + ' ' + error.error;
    }
    res.error(JSON.stringify(error));
  });

});


var getGoogleAccessToken = function(code) {
  var body = querystring.stringify({
    access_token: code
  });
  return Parse.Cloud.httpRequest({
    url: googleValidateEndpoint + '?access_token=' + code
  });
}


var upsertGoogleUser = function(accessToken, googleData, emailId) {
  var query = new Parse.Query(TokenStorage);
  query.equalTo('accountId', googleData.id);
  //query.ascending('createdAt');
  // Check if this googleId has previously logged in, using the master key
  return query.first({ useMasterKey: true }).then(function(tokenData) {
    // If not, create a new user.
    if (!tokenData) {
      return newGoogleUser(accessToken, googleData, emailId);
    }
    // If found, fetch the user.
    var user = tokenData.get('user');
    return user.fetch({ useMasterKey: true }).then(function(user) {
      // Update the access_token if it is different.
      if (accessToken !== tokenData.get('accessToken')) {
        tokenData.set('accessToken', accessToken);
      }
      /**
       * This save will not use an API request if the token was not changed.
       * e.g. when a new user is created and upsert is called again.
       */
      return tokenData.save(null, { useMasterKey: true });
    }).then(function(obj) {
      // Reset password
      password = new Buffer(24);
      _.times(24, function(i) {
          password.set(i, _.random(0, 255));
      });
      password = password.toString('base64')
      user.setPassword(password);
      return user.save();
    }).then(function(user) {
      // ReLogin  
      // This line is what I am talking about
      return Parse.User.logIn(user.get('username'), password);  
    }).then(function(obj) {
      // Return the user object.
      return Parse.Promise.as(obj);
    });
  });
}



var newGoogleUser = function(accessToken, googleData, email) {
  var user = new Parse.User();
  // Generate a random username and password.
  var username = new Buffer(24);
  var password = new Buffer(24);
  _.times(24, function(i) {
    username.set(i, _.random(0, 255));
    password.set(i, _.random(0, 255));
  });
  var name = googleData.name;
  // name = name.split(" ");
  // var fullname = name;
  // if(name.length > 1)
  // var lastName = name[name.length-1];
  user.set("username", username.toString('base64'));
  user.set("password", password.toString('base64'));
  user.set("email", email);
  user.set("fullName", name);
  // user.set("last_name", lastName);
  user.set("accountType", 'google');
  // Sign up the new User
  return user.signUp().then(function(user) {
    // create a new TokenStorage object to store the user+Google association.
    var ts = new TokenStorage();
    ts.set('user', user);
    ts.set('accountId', googleData.id);
    ts.set('accessToken', accessToken);
    ts.setACL(restrictedAcl);
    // Use the master key because TokenStorage objects should be protected.
    return ts.save(null, { useMasterKey: true });
  }).then(function(tokenStorage) {
    return upsertGoogleUser(accessToken, googleData);
  });
}

它完美无缺。现在我面临的问题是我想将谷歌帐户与使用email or username & password创建的现有解析帐户相关联。这样做的问题是,login/signup使用谷歌我必须重置user的密码才能登录以获取会话令牌。在代码中查看此行 - > [这条线就是我所说的]。因此,如果我这样做,之前使用过用户名/电子邮件的现有user&登录密码因为我重置了密码而无法使用电子邮件再次登录。我看过this以及与此相关的所有其他链接,但没有一个解决了这个问题。

有人可以指导我朝正确的方向发展吗?

添加日志作为对其中一条评论的回复:

{"accountType":"google","createdAt":"2016-01-07T17:30:57.429Z","email":"skdkaney@gmail.com","fullName":"ashdakhs basdkbney","updatedAt":"2016-01-07T17:30:57.429Z","username":"owt3h0ZZEZQ1K7if55W2oo3TBLfeWM6m","objectId":"lSlsdsZ9"}

根据评论请求添加了upsert函数:

  var upsertGoogleUser = function(accessToken, googleData, emailId) {
  var query = new Parse.Query(TokenStorage);
  query.equalTo('accountId', googleData.id);
  //query.ascending('createdAt');
  // Check if this googleId has previously logged in, using the master key
  return query.first({ useMasterKey: true }).then(function(tokenData) {
    // If not, create a new user.
    if (!tokenData) {
      return newGoogleUser(accessToken, googleData, emailId);
    }
    // If found, fetch the user.
    var userw = tokenData.get('user');
    var users_id = userw.id;

    var query2 = new Parse.Query(Parse.User);
    query2.equalTo('objectId',users_id);

     // The new query added
    return query2.first({ useMasterKey: true }).then(function(user) {
      // Update the access_token if it is different.
      // if (accessToken !== tokenData.get('accessToken')) {
      //   tokenData.set('accessToken', accessToken);
      // }
      console.log(user);
      console.log("******");
      /**
       * This save will not use an API request if the token was not changed.
       * e.g. when a new user is created and upsert is called again.
       */
      // return tokenData.save(null, { useMasterKey: true });
    }).then(function(obj) {
      console.log(obj);
      // console.log(user);
      var result = user ;
      // Return the user object.
      return Parse.Promise.as(result); // this is the user object acquired above
    });

1 个答案:

答案 0 :(得分:1)

在与OP讨论之后,有可能解决这个问题,但每个问题都有利有弊。

禁用可撤销会话

Revocable Session引入以来,getSessionToken即使使用主密钥也会始终返回undefined。要将其关闭,请转到应用设置>> 用户>>关闭需要撤销会话。 然后,在upsertGoogleUser方法中,您只需要从user返回tokenData.get('user')对象。只需在主云功能中调用user.getSessionToken()即可。最终方法应如下所示:

var upsertGoogleUser = function(accessToken, googleData, emailId) {
    Parse.Cloud.useMasterKey();
    var query = new Parse.Query(TokenStorage);
    query.equalTo('accountId', googleData.id);
    //query.ascending('createdAt');
    // Check if this googleId has previously logged in, using the master key
    return query.first().then(function(tokenData) {
        // If not, create a new user.
        if (!tokenData) {
          return newGoogleUser(accessToken, googleData, emailId);
        }
        // If found, fetch the user.
        var userw = tokenData.get('user');
        var users_id = userw.id;

        var query2 = new Parse.Query(Parse.User);
        query2.equalTo('objectId',users_id);

        return query2.first().then(function(user) {
          console.log(user);
          console.log(user.getSessionToken());
          console.log("******");
          return Parse.Promise.as(user);
        });
    });
};

用户密码输入

为了不更改用户密码,我们可以要求用户在成功验证Google数据后输入密码。然后我们使用输入密码登录用户。这不是一个好的用户体验,因为谷歌登录的目的是通过让用户不输入密码来提高可用性。

查询Parse.Session

如果您想使用"可撤销会话"这是一种可行的解决方案。特征。在上面的代码中,我们可以在Parse.User类中查找任何可撤销的会话,而不是查询Parse.Session。然后我们可以在返回的对象上调用getSessionToken。如果我们需要知道用户登录的设备,这不是最佳解决方案。

参考: