使用Meteor Accounts包链接多个服务

时间:2013-08-21 12:54:33

标签: oauth meteor

因此Meteor拥有这个优秀的帐户包,可以通过密码或其他服务轻松登录。但是,我目前正在创建一个需要多种服务的网络服务(facebook / twitter / etc)。这里的链接:How to add External Service logins to an already existing account in Meteor?通过创建重复的帐户和仅合并数据来建议“黑客”,但对我来说似乎非常不满意。

所以我的问题是:

1)是否有更优雅的方式使用Accounts-xxx包创建一个用户但附加了多个服务?

2)如果没有,我可以使用现在分开的oauth包来向一个用户添加令牌。例如,如果github令牌在以后手动“附加”,那么Accounts.loginWithGithub是否仍会在以后找到手动合并的帐户?

感谢您的帮助。

2 个答案:

答案 0 :(得分:27)

解决!

我通过逆向工程accounts-oauth-X包解决了这个问题!处理所有边缘情况的最佳解决方案是有一个用于登录的故事,另一个用于显式关联。以下是用文字coffeescript编写的解决方案。

之前:

确保您拥有所需的套餐:

meteor add google
meteor add facebook
meteor add oauth

具有多服务登录的方法:

如果他们拥有相同的电子邮件,则会自动加入帐户。最适合登录,因为它很强大。但是,对于显式关联不好,因为如果电子邮件地址不同,那么它将无效。 (并且手动合并会将用户注销并强制您重新登录)。要明确关联帐户,即使帐户有不同的电子邮件,请参阅下面的解决方案。

服务器端代码:

Accounts.onCreateUser (options, user) ->
  user.city = null
  user.networks = []
  user.attending =[]
  user.profile ?= {}

如果用户通过oauth服务注册,我们需要采取额外的逻辑,这样做 我们可以标准化名称和电子邮件的位置。

  if user.services?
    service = _.keys(user.services)[0]

检查是否有任何现有帐户已有与之关联的电子邮件 服务。如果是这样,只需将用户信息合并到。

    email = user.services[service].email
    if email?
      oldUser = Meteor.users.findOne({"emails.address": email})

确保旧帐户具有所需的服务对象。

      if oldUser?
        oldUser.services ?= {}
        if service == "google" or service == "facebook"

将新服务密钥合并到我们的旧用户中。我们还检查是否有新的 要添加到用户的电子邮件。然后从DB中删除旧用户 再次返回以直接再次创建它。假设的新用户 被创建被丢弃。

          oldUser.services[service] = user.services[service]
          Meteor.users.remove(oldUser._id)
          user = oldUser

否则只需正常创建用户,标准化电子邮件和名称字段。

      else
        if service == "google" or service == "facebook"
          if user.services[service].email?
            user.emails = [{address: user.services[service].email, verified: true}]
          else
            throw new Meteor.Error(500, "#{service} account has no email attached")
          user.profile.name = user.services[service].name

  return user

显式关联的方法:

这会将服务添加到用户记录上的服务哈希中,就像它是由内置哈希系统创建的一样。代码需要是客户端(获取服务oauth令牌)和部分服务器端(进行进一步的API调用以获取用户数据并将其与用户记录相关联)

客户端代码:

此功能是将功能添加到我们帐户的入口点。

addUserService = (service) ->

如果他们选择电子邮件,我们需要使用Meteor的内置电子邮件验证系统。

  if service == "email"

  else
    switch service

对于标准的oauth服务,我们在客户端请求凭据,并且 然后将它们传递给服务器以进行进一步的API调用以收集 必要的信息并将该信息添加到我们的帐户。

      when "facebook"
        Facebook.requestCredential(
          requestPermissions: ["email", "user_friends", "manage_notifications"],
        , (token) ->
          Meteor.call "userAddOauthCredentials", token, Meteor.userId(), service, (err, resp) ->
            if err?
              Meteor.userError.throwError(err.reason)
        )
      when "google"
        Google.requestCredential
          requestPermissions: ["email", "https://www.googleapis.com/auth/calendar"]
          requestOfflineToken: true,
        , (token) ->
          Meteor.call "userAddOauthCredentials", token, Meteor.userId(), service, (err, resp) ->
            if err?
              Meteor.userError.throwError(err.reason)

我们只需要设置一个简单的绑定将它粘合在一起。

Template.userAddServices.events
  "click button": (e) ->
    e.preventDefault()
    service = $(event.target).data("service")
    addUserService(service)

服务器端代码:

这里我们定义与用户模型相关的服务器端方法。

Meteor.methods

这会从客户端传递数据,后者抓取访问令牌 已经。使用令牌发现有关用户的其余信息 在那个服务上。然后检查该帐户是否已经注册, 如果没有 - 将其添加到当前帐户。

  userAddOauthCredentials: (token, userId, service) ->
    switch service
      when "facebook"
        data = Facebook.retrieveCredential(token).serviceData
      when "google"
        data = Google.retrieveCredential(token).serviceData

    selector = "services.#{service}.id"
    oldUser = Meteor.users.findOne({selector: data.id})
    if oldUser?
      throw new Meteor.Error(500, "This #{service} account has already" +
      "been assigned to another user.")

    updateSelector = "services.#{service}"
    Meteor.users.update(userId, {$set: {updateSelector: data }})

我们也可以通过此帐户发送电子邮件。如果还没有 当前用户,我们可以抓住并添加它。

    if not _.contains(Meteor.user().emails, data.email)
      Meteor.users.update(userId, {$push: {"emails": {address: data.email, verified: true} }})

答案 1 :(得分:2)

删除现有用户可能会在某些情况下产生问题。在我的用例中,我需要合并谷歌帐户,同时使用另一个oauth帐户登录系统。这可以通过以下修改来实现。

修改Accounts.onCreateUser

Accounts.onCreateUser(function(options,user){

if ( user.services )
    service = _.keys( user.services )[0]; // get service type

var email = user.services[service].email ;
var oldUser = Meteor.users.findOne({ 'emails.address': email });

if ( oldUser ){
    oldUser.services = oldUser.services ? oldUser.services : {};

    oldUser.services[service] = user.services[ service ];     
    return oldUser;     

}

return user;

});

修改基于帐户的包

userId = Meteor.users.insert(fullUser); 
package / accounts-base / accounts_server.js的Accounts.insertUserDoc函数中的

行应替换为以下代码。希望流星将解决这个问题。直到必须使用自定义包。

if(Meteor.users.findOne(fullUser._id)){
  userId = fullUser._id;
  delete fullUser._id;
  Meteor.users.update(userId, {$set: fullUser});
} else
  userId = Meteor.users.insert(fullUser);