15

在我的計算機上與SSL issues掙扎後,我仍然試圖通過Google Ruby客戶端API訪問用戶的Blogger帳戶。我使用了以下內容:Rails Google客戶端API - 無法爲訪問令牌交換刷新令牌

  • 的Rails 3.2.3
  • 的Ruby 1.9.3
  • 的oauth2(0.8.0)
  • omniauth(1.1.1)
  • omniauth - 谷歌 - 的oauth2 (0.1.13)
  • 谷歌的API客戶端(0.4.6)

我能成功驗證的用戶,並通過谷歌訪問一個自己的博客PI在認證時。當用戶登錄時,我會存儲我從Google收到的access_tokenrefresh_token。並且一切都很好,直到access_token到期。我試圖建立功能,交換refresh_token爲新的access_token,但繼續對牆壁。使用client documentation作爲一個例子,這是我使用的代碼:

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

    # Load the access token if it's 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是有效的。只要它雖然到期,我看到兩個問題:

  1. 儘管我知道令牌已過期(我已經在數據庫中檢查expires_at值),client.authorization.expired?回報false - 有一種不同的方式除了使用數據庫中的值之外,我可以檢查令牌的到期日期嗎?
  2. 當我強制執行client.authorization.fetch_access_token!時,出現invalid_request錯誤。

有人可以讓我知道如何使用客戶端API將refresh_token換成access_token?即使你知道如何用另一種語言來做,這將是一個很大的幫助,因爲我可以嘗試Rubyfy它。謝謝!!

回答

25

您可能已經發現了這一點,但你可以在整個過程在這裏谷歌閱讀:https://developers.google.com/accounts/docs/OAuth2WebServer

的omniauth - 谷歌 - 的oauth2戰略已採取設置ACCESS_TYPE的關懷和approval_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 
+0

brimil01,你是男人!我甚至不知道爲什麼會這樣,我的其他嘗試進行HTTP調用沒有,但我一直在看這個12個小時,所以我明天就會明白。你的代碼完全可以工作,你已經從大規模的頭撞中拯救了我,非常感謝。 – cerrina

+0

謝謝。一直試圖弄清楚這一點。 –

+0

我發佈這個時似乎得到了這個: #「invalid_request」},@response =# ,@headers = {「cache-control」=> [「no-cache,no-store,max-age = 0,must-revalidate」],「pragma」=> [「no-cache」],「expires」 =「[」Fri,01 Jan 01 00:00:00 GMT「],」date「=> [」Wed,27 Mar 2013 08:50:48 GMT「],」content-type「=> [」application/json「],」x-content-type-options「=> [」nosniff「],」x-frame-options「=> [」SAMEORIGIN「],」x-xss-protection「=> [」1; mode ='block'],「server」=> [「GSE」],「connection」=> [「close」]}> ... –

16

由於您使用的是Ruby Google API客戶端,爲什麼不使用它來交換刷新令牌呢? Ruby API在內部做了幾乎相同的事情,@ 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 

而且根據this issue here,建議不要使用expired?方法來檢查,如果一個訪問令牌已過期。

基本上不要叫過期?方法。在這種情況下,這基本上是零的情況,這是一個不錯的主意。它根本不會給你可靠的 到期信息。這更像是一個暗示,而不是實際到期 時間戳,並且令牌服務器無論如何可以決定在某些有些理論上但重要的情況下兌現過期令牌 。 如果您確實收到無效授權錯誤,請始終刷新您的訪問令牌 並重試一次。如果您仍然收到錯誤,請提出錯誤。

這是我做的。

# Retrieved stored credentials for the provided user email address. 
# 
# @param [String] email_address 
# User's 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's database. 
# 
# @param [String] user_id 
# User's 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 
+1

在'credentials_expired?'中,調用'client。 execute'不應該使用'execute!'形式,因爲如果憑證過期而不是使用未授權狀態填充'result',它將引發異常。 – pauljm

0

我用下面的簡單代碼修復了它。

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 
相關問題