web-dev-qa-db-ja.com

RailsアプリケーションでOmniauth-oauth2を使用してトークンを更新する

Railsでoauth2をサポートするサイトへの認証にomniauth-oauth2を使用しています。oauthダンスを実行すると、サイトから次のようになります。次に、データベースに永続化します。

  1. アクセストークン
  2. Expires_AT(ティック)
  3. トークンを更新

トークンの有効期限が切れた後にトークンを自動的に更新するomniauthメソッドはありますか、それとも同じことを行うカスタムコードを記述する必要がありますか?

カスタムコードを記述する場合、ヘルパーはロジックを記述するのに適切な場所ですか?

29
ganeshran

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(または開発)で設定できる環境変数です

20
ganeshran

実際、 omniauth-oauth2 gem とその依存関係 oauth2 には、どちらもいくつかの更新ロジックが組み込まれています。

参照してください https://github.com/intridea/oauth2/blob/master/lib/oauth2/access_token.rb#L8

# 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!
19
Eero

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
6
Nick

ここにはいくつかの情報がありますが、リストするには多すぎます ここ 。使用しているプロバイダーと、refresh-tokenの許可された使用法によって異なる場合があります

5
Shane O'Grady

他の回答と同様に、このアプローチに従いました。認証と更新トークンを格納するモデルが使用され、そのロジックからAPIの相互作用を抽象化します。

参照 https://stackoverflow.com/a/51041855/1392282

0
Nuno Silva