2014-11-26 9 views
6

Buduję stronę internetową + backend z FLask Framework, w której używam Flask-OAuthlib do uwierzytelniania za pomocą google. Po uwierzytelnieniu backend musi regularnie skanować użytkownika swoim Gmailem. Obecnie użytkownicy mogą uwierzytelniać moją aplikację i przechowywać access_token i refresh_token. access_token wygasa po jednej godzinie, więc w ciągu tej jednej godziny można uzyskać userinfo tak:Jak korzystać z refresh_token, aby uzyskać nowy access_token (za pomocą Flask-OAuthLib)?

google = oauthManager.remote_app(
     'google', 
     consumer_key='xxxxxxxxx.apps.googleusercontent.com', 
     consumer_secret='xxxxxxxxx', 
     request_token_params={ 
      'scope': ['https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/gmail.readonly'], 
      'access_type': 'offline' 
     }, 
     base_url='https://www.googleapis.com/oauth2/v1/', 
     request_token_url=None, 
     access_token_method='POST', 
     access_token_url='https://accounts.google.com/o/oauth2/token', 
     authorize_url='https://accounts.google.com/o/oauth2/auth' 
    ) 

token = (the_stored_access_token, '') 
userinfoObj = google.get('userinfo', token=token).data 
userinfoObj['id'] # Prints out my google id 

Gdy godzin dobiegnie końca, muszę użyć refresh_token (który mam zapisany w mojej bazy danych) aby poprosić o nowy access_token. Próbowałem zastąpić the_stored_access_token z the_stored_refresh_token, ale to po prostu daje mi Invalid Credentials -error.

W this github issue czytam co następuje:

niezależnie od tego, w jaki sposób uzyskał dostęp tokena/odświeżania tokena (czy to poprzez dotację kod autoryzacji lub zasób haseł właściciel mandatów), wymieniać je w ten sam sposób, przez przekazanie odświeżającego tokenu jako refresh_token i grant_type ustawionego na "refresh_token".

Z tego zrozumiałem, musiałem stworzyć zdalną aplikację tak:

google = oauthManager.remote_app(
     'google', 
     # also the consumer_key, secret, request_token_params, etc.. 
     grant_type='refresh_token', 
     refresh_token=u'1/xK_ZIeFn9quwvk4t5VRtE2oYe5yxkRDbP9BQ99NcJT0' 
    ) 

Ale to prowadzi do TypeError: __init__() got an unexpected keyword argument 'refresh_token'. Odtąd jestem trochę zagubiony.

Czy ktoś wie, jak mogę użyć refresh_token, aby uzyskać nowy access_token? Wszystkie wskazówki są mile widziane!

Odpowiedz

0

Patrząc na kod źródłowy dla OAuthRemoteApp. Konstruktor nie przyjmuje argumentu słowa kluczowego o nazwie refresh_token. Jednakże bierze on argument o nazwie access_token_params, który jest an optional dictionary of parameters to forward to the access token url.

Ponieważ adres URL jest taki sam, ale typ przydziału jest inny. Wyobrażam wywołanie jak to powinno działać:

google = oauthManager.remote_app(
    'google', 
    # also the consumer_key, secret, request_token_params, etc.. 
    grant_type='refresh_token', 
    access_token_params = { 
     refresh_token=u'1/xK_ZIeFn9quwvk4t5VRtE2oYe5yxkRDbP9BQ99NcJT0' 
    } 
) 
1

ten sposób uzyskać nowe access_token na google:

from urllib2 import Request, urlopen, URLError 
from webapp2_extras import json 
import mimetools 
BOUNDARY = mimetools.choose_boundary() 
def refresh_token() 
    url = google_config['access_token_url'] 
    headers = [ 
      ("grant_type", "refresh_token"), 
      ("client_id", <client_id>), 
      ("client_secret", <client_secret>), 
      ("refresh_token", <refresh_token>), 
      ] 

    files = [] 
    edata = EncodeMultiPart(headers, files, file_type='text/plain') 
    headers = {} 
    request = Request(url, headers=headers) 
    request.add_data(edata) 

    request.add_header('Content-Length', str(len(edata))) 
    request.add_header('Content-Type', 'multipart/form-data;boundary=%s' % BOUNDARY) 
    try: 
     response = urlopen(request).read() 
     response = json.decode(response) 
    except URLError, e: 
     ... 

funkcja EncodeMultipart pochodzi stąd: https://developers.google.com/cloud-print/docs/pythonCode

Koniecznie użyj tej samej GRUPOWEJ

0

flask-oauthlib.contrib zawiera parametr o nazwie auto_refresh_url/refresh_token_url w re mote_app, który dokładnie robi to, co chciałeś zrobić. An example jak wykorzystać to wygląda następująco:

app= oauth.remote_app(
    [...] 
    refresh_token_url='https://www.douban.com/service/auth2/token', 
    authorization_url='https://www.douban.com/service/auth2/auth', 
    [...] 
) 

Jednak nie udało mi się dostać to działa w ten sposób. Niemniej jest to możliwe bez pakietu contrib. Moim rozwiązaniem było przechwycenie wywołań API 401 i przekierowanie do odświeżającej strony, jeśli dostępny jest refresh_token. Mój kod dla punktu końcowego odświeżania wygląda następująco:

@app.route('/refresh/') 
def refresh(): 
    data = {} 
    data['grant_type'] = 'refresh_token' 
    data['refresh_token'] = session['refresh_token'][0] 
    data['client_id'] = CLIENT_ID 
    data['client_secret'] = CLIENT_SECRET 
    # make custom POST request to get the new token pair 
    resp = remote.post(remote.access_token_url, data=data) 

    # checks the response status and parses the new tokens 
    # if refresh failed will redirect to login 
    parse_authorized_response(resp) 

    return redirect('/') 

def parse_authorized_response(resp): 
    if resp is None: 
     return 'Access denied: reason=%s error=%s' % (
      request.args['error_reason'], 
      request.args['error_description'] 
     ) 
    if isinstance(resp, dict): 
      session['access_token'] = (resp['access_token'], '') 
      session['refresh_token'] = (resp['refresh_token'], '') 
    elif isinstance(resp, OAuthResponse): 
     print(resp.status) 
     if resp.status != 200: 
      session['access_token'] = None 
      session['refresh_token'] = None 
      return redirect(url_for('login')) 
     else: 
      session['access_token'] = (resp.data['access_token'], '') 
      session['refresh_token'] = (resp.data['refresh_token'], '') 
    else: 
     raise Exception() 
    return redirect('/') 

nadzieję, że pomoże. Kod może być oczywiście ulepszony i na pewno jest bardziej elegancki sposób niż łapanie 401, ale to jest początek;)

Jeszcze jedno: nie przechowuj tokenów w pliku Cookie z flaską. Zamiast tego używaj sesji Server Side z "Flask Session", które zrobiłem w moim kodzie!

Powiązane problemy