Rails API клиентов Google - невозможно обменять токен обновления для токена доступа

После борьбы с некоторыми проблемами SSL на моей машине я все еще пытаюсь получить доступ к учетной записи пользователя Blogger с помощью API клиента Google Ruby. Я использую следующее:

  • Rails 3.2.3
  • Ruby 1.9.3
  • oauth2 (0.8.0)
  • omniauth (1.1.1)
  • omniauth-google-oauth2 (0.1.13)
  • google-api-client (0.4.6)

Я могу успешно аутентифицировать пользователей и получать доступ к их блогам через API Google во время проверки подлинности. Когда пользователь входит в систему, я сохраняю access_token и refresh_token, которые я получаю от Google. и все работает отлично, пока истечет access_token. Я пытаюсь создать функциональность, которая обменивает refresh_token для нового access_token, но все равно прижимается к стенам. Используя клиентскую документацию в качестве примера, это код, который я использую:

  client = Google::APIClient.new
  token_pair = auth.oauth_token   # access_token and refresh_token received during authentication

  # Load the access token if it available
  if token_pair  
    client.authorization.update_token!(token_pair.to_hash)
  end            

  # Update access token if expired
  if client.authorization.refresh_token && client.authorization.expired?
    client.authorization.fetch_access_token!
  end

  blogger = client.discovered_api('blogger', 'v3')
  result = client.execute(
      api_method: blogger.blogs.list_by_user,
      parameters: {'userId' => "self", 'fields' => 'items(description,id,name,url)'},
      headers: {'Content-Type' => 'application/json'})

Этот код работает отлично, пока access_token действителен. Как только он истекает, я вижу 2 проблемы:

  • Даже если я знаю, что токен истек (я проверил значение expires_at в базе данных), client.authorization.expired? возвращает false - есть ли другой способ проверить истечение токена, используя значение в базе данных?
  • Когда я принудительно выполняю выполнение client.authorization.fetch_access_token!, я получаю ошибку invalid_request.

Может кто-нибудь, пожалуйста, дайте мне знать, как я могу обменять refresh_token на новый access_token с помощью клиентского API? Даже если вы знаете, как это сделать на другом языке, это будет большой помощью, так как я могу попробовать Rubyfy. Спасибо!!

Ответы

Ответ 1

Возможно, вы уже нашли это, но вы можете прочитать весь процесс здесь, в google: https://developers.google.com/accounts/docs/OAuth2WebServer

Стратегия omniauth-google-oauth2 уже заботится о настройке access_type и authorized_prompt, поэтому получение токена обновления - это вопрос публикации https://accounts.google.com/o/oauth2/token с помощью grant_type = request_token

Вот примерно код, который я использую:

def refresh_token
  data = {
    :client_id => GOOGLE_KEY,
    :client_secret => GOOGLE_SECRET,
    :refresh_token => REFRESH_TOKEN,
    :grant_type => "refresh_token"
  }
  @response = ActiveSupport::JSON.decode(RestClient.post "https://accounts.google.com/o/oauth2/token", data)
  if @response["access_token"].present?
    # Save your token
  else
    # No Token
  end
rescue RestClient::BadRequest => e
  # Bad request
rescue
  # Something else bad happened
end

Ответ 2

Поскольку вы используете Ruby Google API Client, почему бы не использовать его для обмена токеном обновления? API Ruby делает почти то же самое внутри, что @brimil01 сказал в своем ответе.

Вот как я использую Ruby API для обмена токеном обновления для нового токена доступа.

def self.exchange_refresh_token( refresh_token )
  client = Google::APIClient.new
  client.authorization.client_id = CLIENT_ID
  client.authorization.client_secret = CLIENT_SECRET
  client.authorization.grant_type = 'refresh_token'
  client.authorization.refresh_token = refresh_token

  client.authorization.fetch_access_token!
  client.authorization
end

И в соответствии с этой проблемой здесь, рекомендуется не использовать метод expired? для проверки того, истек ли токен доступа.

В принципе, не вызывать срок действия истек? метод. Есть, по существу, ноль сценарии, где это хорошая идея. Это просто не даст вам надежного информация об истечении срока действия. Это скорее намек, чем реальное истечение временная метка, а маркерный сервер может принять решение соблюдать истекший токен в любом случае в некоторых несколько теоретических, но важных обстоятельствах. Если вы получаете недопустимую ошибку гранта, всегда обновляйте токен доступа и повторить один раз. Если вы все еще получаете ошибку, поднимите ошибку.

Вот что я делаю.

# Retrieved stored credentials for the provided user email address.
#
# @param [String] email_address
#   User email address.
# @return [Signet::OAuth2::Client]
#  Stored OAuth 2.0 credentials if found, nil otherwise.
def self.get_stored_credentials(email_address)
  hash = Thread.current['google_access_token']
  return nil if hash.blank?

  hash[email_address]
end

##
# Store OAuth 2.0 credentials in the application database.
#
# @param [String] user_id
#   User ID.
# @param [Signet::OAuth2::Client] credentials
#   OAuth 2.0 credentials to store.
def self.store_credentials(email_address, credentials)
  Thread.current['google_access_token'] ||= {}
  Thread.current['google_access_token'][email_address] = credentials
end


def self.credentials_expired?( credentials )
  client = Google::APIClient.new
  client.authorization = credentials
  oauth2 = client.discovered_api('oauth2', 'v2')
  result = client.execute!(:api_method => oauth2.userinfo.get)

  (result.status != 200)
end


# @return [Signet::OAuth2::Client]
#  OAuth 2.0 credentials containing an access and refresh token.
def self.get_credentials
  email_address = ''

  # Check if a valid access_token is already available.
  credentials = get_stored_credentials( email_address )
  # If not available, exchange the refresh_token to obtain a new access_token.

  if credentials.blank?
    credentials = exchange_refresh_token(REFRESH_TOKEN)
    store_credentials(email_address, credentials)
  else
    are_credentials_expired = credentials_expired?(credentials)

    if are_credentials_expired
      credentials = exchange_refresh_token(REFRESH_TOKEN)
      store_credentials(email_address, credentials)
    end
  end

  credentials
end

Ответ 3

Я установил его с помощью простого кода ниже.

   def refesh_auth_tooken(refresh_token) 
       client = Google::APIClient.new 
       puts "REFESH TOOKEN"
       client.authorization = client_secrets
       client.authorization.refresh_token = refresh_token

       #puts YAML::dump(client.authorization)

       client.authorization.fetch_access_token!
       return client.authorization

     end