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