15

Dopo aver lottato con alcuni SSL issues sul mio computer, sto ancora tentando di accedere all'account Blogger di un utente tramite l'API di Google Ruby Client. Sto usando il seguente:- impossibile scambiare un token di aggiornamento per token di accesso

  • Rails 3.2.3
  • Rubino 1.9.3
  • OAuth2 (0.8.0)
  • omniauth (1.1.1)
  • omniauth-google-OAuth2 (0.1.13)
  • google-api-client (0.4.6)

mi può autenticare con successo gli utenti e accedere ai propri blog attraverso il Google A PI al momento dell'autenticazione. Quando un utente effettua l'accesso, memorizzo access_token e refresh_token da Google. e tutto funziona alla grande fino allo scadere del access_token. Sto cercando di costruire la funzionalità che scambia il refresh_token per un nuovo access_token, ma continui a venire contro i muri. Utilizzando la client documentation come esempio, questo è il codice che sto utilizzando:

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'}) 

Questo codice funziona perfettamente mentre il access_token è valido. Non appena scade però, mi sto vedendo 2 problemi:

  1. Anche se so che il token è scaduto (ho controllato expires_at valore nel database), client.authorization.expired? rendimenti false - c'è un modo diverso Posso controllare la scadenza del token oltre a usare il valore nel database?
  2. Quando imposto l'esecuzione di client.authorization.fetch_access_token!, viene visualizzato un errore invalid_request.

Qualcuno può per favore fatemelo sapere come posso scambiare un refresh_token per un nuovo access_token utilizzando l'API client? Anche se sapessi come farlo in un'altra lingua, sarebbe un grande aiuto in quanto posso quindi provare a Rubyfy. Grazie!!

risposta

25

Si può avere già trovato questo, ma si può leggere attraverso l'intero processo qui a google: https://developers.google.com/accounts/docs/OAuth2WebServer

La strategia omniauth-google-OAuth2 prende già cura di impostare il tipo_accesso e approval_prompt in modo da ottenere un token di aggiornamento è solo una questione di distacco per https://accounts.google.com/o/oauth2/token con grant_type = request_token

Qui è all'incirca il codice che uso:

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, sei l'uomo !! Non so nemmeno perché funzioni e gli altri miei tentativi di effettuare chiamate HTTP no, ma l'ho guardato per 12 ore, quindi lo scoprirò domani. Il tuo codice funziona totalmente e tu mi hai salvato da un enorme head-banging, molto apprezzato. – cerrina

+0

grazie. ho cercato di capirlo per un po '. –

+0

Mi sembra di ottenere questo quando inserisco questo: # "invalid_request"}, @response = # , @headers = {"cache-control" => ["no-cache, no-store, max-age = 0, must-revalidate"], "pragma" => ["no-cache"], "scade" => ["Fri, 01 Jan 1990 00:00:00 GMT"], "date" => ["Mer 27 mar 2013 08:50:48 GMT"], "content-type" => ["application/json "]," x-content-type-options "=> [" nosniff "]," x-frame-options "=> [" SAMEORIGIN "]," x-xss-protection "=> [" 1; modalità = block "]," server "=> [" GSE "]," connection "=> [" close "]}> ... –

16

Poiché stai utilizzando il client API di Ruby Google, perché non utilizzarlo per scambiare anche il token di aggiornamento? L'API di Ruby fa praticamente la stessa cosa internamente, che @ brimil01 ha detto nella sua risposta.

Ecco come uso l'API Ruby per scambiare il mio token di aggiornamento per un nuovo token di accesso.

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 

E secondo this issue here, si raccomanda di non utilizzare il metodo expired? per controllare se un token di accesso è scaduta.

Fondamentalmente, non chiamare il scaduto? metodo. Ci sono essenzialmente zero scenari dove questa è una buona idea. Semplicemente non ti darà informazioni affidabili sulla scadenza . È più un suggerimento che una data di scadenza reale e il server token può decidere di onorare un token scaduto in alcune circostanze piuttosto teoriche, ma importanti. Se si verifica un errore di concessione non valido, aggiornare sempre il token di accesso e riprovare una volta. Se ricevi ancora un errore, genera l'errore.

Ecco cosa faccio.

# 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

In' credentials_expired? ', la chiamata a' client. execute' non dovrebbe usare il form 'execute!', poiché solleverà un'eccezione se le credenziali sono scadute invece di compilare 'result' con lo stato non autorizzato. – pauljm

0

L'ho risolto con un semplice codice di seguito.

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 
Problemi correlati