在 Rails 应用程序中使用 Omniauth-oauth2 刷新令牌

Posted

技术标签:

【中文标题】在 Rails 应用程序中使用 Omniauth-oauth2 刷新令牌【英文标题】:Refresh token using Omniauth-oauth2 in Rails application 【发布时间】:2014-03-09 14:04:11 【问题描述】:

我在 rails 中使用omniauth-oauth2 对支持oauth2 的站点进行身份验证。完成 oauth dance 后,该网站给了我以下信息,然后我将其保存到数据库中:

    访问令牌 Expires_AT(滴答声) 刷新令牌

是否有一种omniauth 方法可以在令牌过期后自动刷新它,或者我应该编写自定义代码来做同样的事情吗?

如果要编写自定义代码,助手是否适合编写逻辑?

【问题讨论】:

【参考方案1】:

Omniauth 不提供开箱即用的此功能,因此我使用上一个答案和另一个 SO 答案在我的模型中编写代码 User.rb

def refresh_token_if_expired
  if token_expired?
    response    = RestClient.post "#ENV['DOMAIN']oauth2/token", :grant_type => 'refresh_token', :refresh_token => self.refresh_token, :client_id => ENV['APP_ID'], :client_secret => ENV['APP_SECRET'] 
    refreshhash = JSON.parse(response.body)

    token_will_change!
    expiresat_will_change!

    self.token     = refreshhash['access_token']
    self.expiresat = DateTime.now + refreshhash["expires_in"].to_i.seconds

    self.save
    puts 'Saved'
  end
end

def token_expired?
  expiry = Time.at(self.expiresat) 
  return true if expiry < Time.now # expired token, so we should quickly return
  token_expires_at = expiry
  save if changed?
  false # token not expired. :D
end

在使用访问令牌进行 API 调用之前,您可以调用这样的方法,其中 current_user 是登录用户。

current_user.refresh_token_if_expired

确保安装 rest-client gem 并在模型文件中添加 require 指令 require 'rest-client'ENV['DOMAIN']ENV['APP_ID']ENV['APP_SECRET'] 是可以在config/environments/production.rb(或开发)中设置的环境变量

【讨论】:

ENV['DOMAIN'] 应该是什么样子?【参考方案2】:

事实上,omniauth-oauth2 gem 及其依赖项oauth2 都内置了一些刷新逻辑。

见https://github.com/intridea/oauth2/blob/master/lib/oauth2/access_token.rb#L80

# Refreshes the current Access Token
#
# @return [AccessToken] a new AccessToken
# @note options should be carried over to the new AccessToken
def refresh!(params = )
  fail('A refresh_token is not available') unless refresh_token
  params.merge!(:client_id      => @client.id,
                :client_secret  => @client.secret,
                :grant_type     => 'refresh_token',
                :refresh_token  => refresh_token)
  new_token = @client.get_token(params)
  new_token.options = options
  new_token.refresh_token = refresh_token unless new_token.refresh_token
  new_token
end

在https://github.com/intridea/omniauth-oauth2/blob/master/lib/omniauth/strategies/oauth2.rb#L74:

self.access_token = access_token.refresh! if access_token.expired?

因此,您可能无法直接使用omniauth-oauth2 执行此操作,但您当然可以使用oauth2 执行类似的操作:

client = strategy.client # from your omniauth oauth2 strategy
token = OAuth2::AccessToken.from_hash client, record.to_hash
# or
token = OAuth2::AccessToken.new client, token, expires_at: 123456789, refresh_token: "123"
token.refresh!

【讨论】:

有没有一种简单的方法可以从 Rails 应用程序中的现有策略加载客户端?我最终像 Oauth2 策略一样声明了一个新客户端:github.com/intridea/omniauth-oauth2/blob/master/lib/omniauth/… 我猜你的问题是,我如何找到对现有 OmniAuth 策略的引用?一旦你有了策略对象,它就有一个client 方法。以下是其他人询问寻找策略的问题:***.com/questions/13112430/… 感谢@Eero - 这使我在下面进行了修复:***.com/a/38276378/224707【参考方案3】:

Eero 的回答为我解决了这个问题。我对我的课程有帮助,这让我获得了 GmailService。作为此过程的一部分,如果用户对象(包含 google 身份验证信息)已过期,则会对其进行检查。如果有,它会在返回服务之前刷新。

def gmail_service(user)
  mail = Google::Apis::GmailV1::GmailService.new

  # Is the users token expired?
  if user.google_token_expire.to_datetime.past?
    oauth = OmniAuth::Strategies::GoogleOauth2.new(
      nil, # App - nil seems to be ok?!
      "XXXXXXXXXX.apps.googleusercontent.com", # Client ID
      "ABC123456" # Client Secret
    )
    token = OAuth2::AccessToken.new(
      oauth.client,
      user.google_access_token,
       refresh_token: user.google_refresh_token 
    )
    new_token = token.refresh!

    if new_token.present?
      user.update(
        google_access_token: new_token.token,
        google_token_expire: Time.at(new_token.expires_at),
        google_refresh_token: new_token.refresh_token
      )
    else
      puts("DAMN - DIDN'T WORK!")
    end
  end

  mail.authorization = user.google_access_token

  mail
end

【讨论】:

OmniAuth::Strategies::YOUR_PROVIDER(nil, client_id, secret)...这是访问策略及其客户端的唯一方法吗?【参考方案4】:

这里有一些信息,太多了,无法列出here。这可能取决于您使用的提供商,以及他们允许使用的refresh-token

【讨论】:

【参考方案5】:

与其他答案类似,我采用了这种方法,其中使用了存储身份验证和刷新令牌的模型,从该逻辑中抽象出 API 交互。

见https://***.com/a/51041855/1392282

【讨论】:

以上是关于在 Rails 应用程序中使用 Omniauth-oauth2 刷新令牌的主要内容,如果未能解决你的问题,请参考以下文章

Rails - 使用 Omniauth 和 Koala 的 Facebook:如何更新过期的令牌

Rails Oauth 设计 Omniauth

Rails - 设计/Omniauth - 无方法错误配置

Ruby on rails omniauth-twitter 和设计

Rails Devise Omniauth new_user_registration_url

OAuth::Unauthorized 401 Unauthorized for omniauth-twitter in rails