【问题标题】:Omniauth-Facebook: How to get Long-Lived Access Tokens?Omniauth-Facebook:如何获得长期访问令牌?
【发布时间】:2015-08-05 08:32:25
【问题描述】:

我跟随伟大的 Ryan Bates 帮助我设置 ominauth-facebook,但在他的教程中,他只展示了如何使用短期访问令牌(小时或 2 小时)进行设置。我们如何修改他的教程以使用长期访问令牌(60 天)进行设置?

我一直在阅读Facebook docs about it 和这个较旧的SO question

两个链接都建议添加一些这样的代码(尽管每个链接的代码略有不同):

GET /oauth/access_token?  
    grant_type=fb_exchange_token&           
    client_id={app-id}&
    client_secret={app-secret}&
    fb_exchange_token={short-lived-token}

将这个添加到哪里?

从我读到的内容看来,他们让这件事变得比应有的复杂得多。有人可以用初学者友好的语言来表达吗?

让我们来个人化吧。

我的代码与贝茨先生略有不同。

initializers/ominauth.rb

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :facebook, "1540352575225959", "ee957abf5e851c98574cdfaebb1355f4", {:scope => 'user_about_me'}
end

routes.rb

get 'auth/:provider/callback', to: 'sessions#facebook'

sessions_controller

  def facebook
    user = User.from_omniauth(env["omniauth.auth"])
    session[:user_id] = user.id
    redirect_to root_url
  end

user.rb

class User < ActiveRecord::Base
  acts_as_tagger
  acts_as_taggable
  has_many :notifications
  has_many :activities
  has_many :liked_comments, through: :comment_likes, class_name: 'Comment', source: :liked_comment
  has_many :valuation_likes
  has_many :habit_likes
  has_many :goal_likes
  has_many :stat_likes
  has_many :comment_likes
  has_many :authentications
  has_many :habits, dependent: :destroy
  has_many :levels
  has_many :valuations, dependent: :destroy
  has_many :comments
  has_many :goals, dependent: :destroy
  has_many :stats, dependent: :destroy
  has_many :results, through: :stats
  has_many :notes
  accepts_nested_attributes_for :habits, :reject_if => :all_blank, :allow_destroy => true
  accepts_nested_attributes_for :notes, :reject_if => :all_blank, :allow_destroy => true
  accepts_nested_attributes_for :stats, :reject_if => :all_blank, :allow_destroy => true
  accepts_nested_attributes_for :results, :reject_if => :all_blank, :allow_destroy => true
  has_many :active_relationships, class_name:  "Relationship",
                                  foreign_key: "follower_id",
                                  dependent:   :destroy
  has_many :passive_relationships, class_name:  "Relationship",
                                   foreign_key: "followed_id",
                                   dependent:   :destroy
  has_many :following, through: :active_relationships,  source: :followed
  has_many :followers, through: :passive_relationships, source: :follower
  attr_accessor :remember_token, :activation_token, :reset_token
  before_save   :downcase_email
  before_create :create_activation_digest
  validates :name,  presence: true, length: { maximum: 50 }, format: { with: /\A[a-z\sA-Z]+\z/,
    message: "only allows letters" }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 },
                    format: { with: VALID_EMAIL_REGEX },
                    uniqueness: { case_sensitive: false }, unless: -> { from_omniauth? }
  has_secure_password
  validates :password, length: { minimum: 6 }

  def name
     read_attribute(:name).try(:titleize)
  end

  def count_mastered
    @res = habits.reduce(0) do |count, habit|
    habit.current_level == 6 ? count + 1 : count
    end
  end

  def count_challenged
    @challenged_count = habits.count - @res
  end

    def self.from_omniauth(auth)
      where(provider: auth.provider, uid: auth.uid).first_or_initialize.tap do |user|
        user.provider = auth.provider
        user.image = auth.info.image
        user.uid = auth.uid
        user.name = auth.info.name
        user.oauth_token = auth.credentials.token
        user.oauth_expires_at = Time.at(auth.credentials.expires_at)
        user.password = (0...8).map { (65 + rand(26)).chr }.join
        user.email = (0...8).map { (65 + rand(26)).chr }.join+"@mailinator.com"
        user.save!
      end
    end

  def self.koala(auth)
    access_token = auth['token']
    facebook = Koala::Facebook::API.new(access_token)
    facebook.get_object("me?fields=name,picture")
  end


  # Returns the hash digest of the given string.
  def User.digest(string)
    cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
                                                  BCrypt::Engine.cost
    BCrypt::Password.create(string, cost: cost)
  end

  # Returns a random token.
  def User.new_token
    SecureRandom.urlsafe_base64
  end

  # Remembers a user in the database for use in persistent sessions.
  def remember
    self.remember_token = User.new_token
    update_attribute(:remember_digest, User.digest(remember_token))
  end

  # Forgets a user. NOT SURE IF I REMOVE
  def forget
    update_attribute(:remember_digest, nil)
  end

  # Returns true if the given token matches the digest.
  def authenticated?(attribute, token)
    digest = send("#{attribute}_digest")
    return false if digest.nil?
    BCrypt::Password.new(digest).is_password?(token)
  end

  # Activates an account.
  def activate
    update_attribute(:activated,    true)
    update_attribute(:activated_at, Time.zone.now)
  end

  # Sends activation email.
  def send_activation_email
    UserMailer.account_activation(self).deliver_now
  end

  def create_reset_digest
    self.reset_token = User.new_token
    update_attribute(:reset_digest,  User.digest(reset_token))
    update_attribute(:reset_sent_at, Time.zone.now)
  end

  # Sends password reset email.
  def send_password_reset_email
    UserMailer.password_reset(self).deliver_now
  end

   # Returns true if a password reset has expired.
  def password_reset_expired?
    reset_sent_at < 2.hours.ago
  end

  def good_results_count
    results.good_count
  end

  # Follows a user.
  def follow(other_user)
    active_relationships.create(followed_id: other_user.id)
  end

  # Unfollows a user.
  def unfollow(other_user)
    active_relationships.find_by(followed_id: other_user.id).destroy
  end

  # Returns true if the current user is following the other user.
  def following?(other_user)
    following.include?(other_user)
  end

private 

    def from_omniauth? 
      provider && uid 
    end

      # Converts email to all lower-case.
    def downcase_email 
      self.email = email.downcase unless from_omniauth? 
    end

    # Creates and assigns the activation token and digest.
    def create_activation_digest
      self.activation_token  = User.new_token
      self.activation_digest = User.digest(activation_token)
    end
end

facebook.js.coffee.erb

jQuery ->
  $('body').prepend('<div id="fb-root"></div>')

  $.ajax
    url: "#{window.location.protocol}//connect.facebook.net/en_US/all.js"
    dataType: 'script'
    cache: true


window.fbAsyncInit = ->
  FB.init(appId: '<%= 1540372976229929 %>', cookie: true)

  $('#sign_in').click (e) ->
    e.preventDefault()
    FB.login (response) ->
      window.location = '/auth/facebook/callback' if response.authResponse

  if $('#sign_out').length > 0
    FB.getLoginStatus (response) ->
      window.location = $('#sign_out').attr("href") if !response.authResponse

来源: omniauth-facebook

“成为一名优秀的程序员有 3% 的天赋和 97% 的人不会被互联网分心。”

【问题讨论】:

  • 既然你已经在解决问题的路上了,我不会干涉......但我确实遇到了类似的挑战,因为我正在为 Plezi 框架构建 OAuth2 库。你可以看看the code for the final authorization token。它会为您提供一个持续约 30 天的令牌。令牌需要定期更新。除非您更改 OAuth HTTPS 请求的 SSL 验证模式,否则代码容易受到中间人攻击。

标签: ruby-on-rails ruby facebook facebook-graph-api omniauth


【解决方案1】:

在我目前正在构建的应用程序中,我遇到了与您之前遇到的相同问题。我发现的解决方案建议在创建后立即使用 Koala 身份验证来延长初始短期令牌。

对我来说效果很好的解决方案的改编如下......

首先,在您的 Gemfile 中包含 gem 'koala', '2.0.0'。另外,请访问Koala github site 了解更多信息。

现在让我们将上面的内容应用到 user.rbfrom_omniauth 方法...

def self.from_omniauth(auth)
   # Sets 60 day auth token
   oauth = Koala::Facebook::OAuth.new("1540352575225959", "ee957abf5e851c98574cdfaebb1355f4")
   new_access_info = oauth.exchange_access_token_info auth.credentials.token

   new_access_token = new_access_info["access_token"]
   new_access_expires_at = DateTime.now + new_access_info["expires"].to_i.seconds

  where(provider: auth.provider, uid: auth.uid).first_or_initialize.tap do |user|
    user.provider = auth.provider
    user.image = auth.info.image
    user.uid = auth.uid
    user.name = auth.info.name
    user.oauth_token = new_access_token # auth.credentials.token <- your old token. Not needed anymore.
    user.oauth_expires_at = Time.at(auth.credentials.expires_at)
    user.password = (0...8).map { (65 + rand(26)).chr }.join
    user.email = (0...8).map { (65 + rand(26)).chr }.join+"@mailinator.com"
    user.save!
  end
end

其他信息:

我还注意到您正在为用户的电子邮件和密码生成一些虚拟值。我可以建议您使用 SecureRandom 库来这样做。虽然您的代码不太可能导致值冲突,但至少对于电子邮件,我建议将(0...8).map { (65 + rand(26)).chr }.join+"@mailinator.com" 更改为更稳定的SecureRandom.hex + "@mailinator.com"。如果您使用电子邮件作为在应用程序中记录用户的方式,这一点尤其重要。

我希望这能解决您的问题。一切顺利,蒂姆。

更新:

我很高兴我们设法回答了您的问题并一起解决了您的所有其他问题,但是这里是关于我之前谈到的重构代码的更新...

考虑一种情况,即通过 Facebook 登录的用户在您的网站上更改了一些关于他们自己的数据。假设他们更改了姓名、电子邮件或头像。这将正常工作直到同一用户再次尝试重新登录。将会发生的是 from_omniauth 方法将再次被触发并覆盖这些更改。这很糟糕,防止这种情况的方法是执行以下操作...

def self.from_omniauth(auth)
  .
  .
  .
  where(provider: auth.provider, uid: auth.uid).first_or_initialize.tap do |user|
    user.provider = auth.provider
    user.image = auth.info.image unless user.image != nil
    user.uid = auth.uid
    user.name = auth.info.name unless user.name != nil
    user.oauth_token = new_access_token 
    user.oauth_expires_at = Time.at(auth.credentials.expires_at)
    user.password = SecureRandom.urlsafe_base64 unless user.password != nil
    user.email = SecureRandom.hex + "@mailinator.com" unless user.email != nil
    user.activated = true
    user.save!
  end

关键是使用unless user.image != nil,它确保只有当:image 的初始值为nil 时才会设置您的图像。如果说用户更改了图像,则该值不会是 nil 并且 from_omniauth 不会更改它。姓名、电子邮件和密码也是如此。不要将其设置为其他任何值。

另外,请注意我使用了SecureRandom.urlsafe_base64 而不是您的(0...8).map { (65 + rand(26)).chr }.join。这是因为urlsafe_base64 会生成一个随机的 URL 安全 base64 字符串,例如 i0XQ-7gglIsHGV2_BNPrdQ==,它非常适合用作虚拟密码,而且看起来也很优雅。

另一方面,我将hex 用于电子邮件的原因是因为它会创建一个随机的十六进制字符串,例如eb693ec8252cd630102fd0d0fb7c3485,如果您碰巧有一些正则表达式来验证电子邮件(这理想情况下你应该)。

【讨论】:

  • 也可以直接使用new_access_token = oauth.get_app_access_token(auth.credentials.token)。只是从信息哈希中提取令牌的辅助方法
【解决方案2】:

您可以使用Koala::Facebook::OAuth 实例中的exchange_access_token_info 方法

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-09
    • 2014-02-27
    • 2012-05-15
    • 1970-01-01
    • 1970-01-01
    • 2014-10-08
    • 2018-07-17
    • 2012-06-04
    相关资源
    最近更新 更多