Подтвердить что ты не робот

Использование пакета Meteor Accounts для соединения нескольких сервисов

Итак, у Meteor этот отличный пакет Accounts, который позволяет легко вводить пароль с помощью пароля или других сервисов. Однако в настоящее время я создаю веб-службу с несколькими требуемыми сервисами (facebook/twitter/etc). Ссылка здесь: Как добавить внешние учетные записи службы в уже существующую учетную запись в Meteor? предлагает "взломать", создавая дубликаты учетных записей и просто объединяя данные, но кажется очень неудовлетворительно для меня.

Итак, мои вопросы:

1) Есть ли более элегантный способ использования пакетов Accounts-xxx для создания одного пользователя, но с несколькими подключенными услугами?

2) Если нет, могу ли я использовать теперь разделенные пакеты oauth, чтобы просто добавлять токены к одному пользователю. Если, например, токен github был "прикреплен" вручную в более поздней точке, будет ли Accounts.loginWithGithub все еще находить, что вручную объединенная учетная запись в более поздней точке?

Спасибо за вашу помощь.

4b9b3361

Ответ 1

Решено!

Я решил проблему путем обратного проектирования пакетов accounts-oauth-X! Оптимальное решение, которое обрабатывает все крайние случаи, состоит в том, чтобы иметь одну историю для логинов, а другую для явных ассоциаций. Вот решения, написанные в грамотном coffeescript.

Перед

Убедитесь, что у вас есть необходимые пакеты:

meteor add google
meteor add facebook
meteor add oauth

Способ использования Multi-Service Login:

Это автоматически присоединяется к учетным записям, если они имеют одинаковое электронное письмо. Лучше всего использовать для входа, потому что он надежный. Однако это не хорошо для явных ассоциаций, потому что, если адреса электронной почты отличаются, то это не сработает. (И ручное слияние приведет к выходу пользователя из системы и заставит вас снова войти в систему). Чтобы явно связать учетную запись, даже если она имеет другое электронное письмо, см. Решение внизу.

Код на стороне сервера:

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"

Слейте новый ключ службы в нашего старого пользователя. Мы также проверяем, есть ли новые чтобы добавить к нашему пользователю. Затем удалите старого пользователя из БД и просто верните его снова, чтобы создать его снова напрямую. Предполагаемый новый пользователь, который должен был быть создан, отбрасывается.

          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} }})

Ответ 2

Удаление существующего пользователя может создавать проблемы в некоторых сценариях. В моем случае использования мне нужно объединить учетную запись google, когда вы входите в систему с другой учетной записью oauth. Это может быть достигнуто со следующими изменениями.

Изменить учетную записьsonCreateUser

Accounts.onCreateUser(функция (параметры, пользователь) {

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); 

в функции Accounts.insertUserDoc пакетов /accounts -base/accounts_server.js следует заменить следующим кодом. Надежда Метеор исправит это. до тех пор, пока не придется использовать пользовательский пакет.

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