2014-11-26 5 views
6

Ich baue eine Website + Backend mit der FLask Framework in der ich Flask-OAuthlib verwenden, um mit Google zu authentifizieren. Nach der Authentifizierung muss das Backend den Benutzer regelmäßig auf sein Google Mail prüfen. So können Benutzer derzeit meine App authentifizieren und ich speichere die access_token und die refresh_token. Die access_token läuft nach einer Stunde so innerhalb dieser einer Stunde ich die Userinfo wie so bekommen kann:Wie verwende ich ein refresh_token, um ein neues access_token zu erhalten (mit 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 

Sobald die Stunde vorbei ist, ich brauche die refresh_token zu verwenden (was ich in meiner Datenbank gespeichert habe) um eine neue access_token anzufordern. Ich habe versucht, the_stored_access_token durch the_stored_refresh_token zu ersetzen, aber das gibt mir einfach einen Invalid Credentials-Fehler.

In this github issue las ich folgendes:

unabhängig davon, wie Sie das Zugriffstoken/Aktualisierungs-Token erhalten (ob durch einen Autorisierungscode Erteilung oder Ressourceneigentümer Kennwortdaten), können Sie sie auf die gleiche Weise austauschen, durch Übergabe des Refresh-Tokens als refresh_token und grant_type als 'refresh_token'.

Daraus ich verstand ich, wie so eine Remote-App erstellen hatte:

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

Aber dies führt zu einer TypeError: __init__() got an unexpected keyword argument 'refresh_token'. Von hier bin ich irgendwie verloren.

Weiß jemand, wie ich die refresh_token verwenden kann, um eine neue access_token zu bekommen? Alle Tipps sind willkommen!

Antwort

0

Blick auf den Quellcode für OAuthRemoteApp. Der Konstruktor nimmt kein Schlüsselwortargument namens refresh_token. Es wird jedoch ein Argument namens access_token_params verwendet, das an optional dictionary of parameters to forward to the access token url ist.

Da die URL ist die gleiche, aber die Gewährung Typ ist anders. Ich stelle mir ein Aufruf wie folgt funktionieren sollte:

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

Dies ist, wie ich ein neues access_token für Google bekommen:

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: 
     ... 

EncodeMultipart Funktion wird von hier genommen: https://developers.google.com/cloud-print/docs/pythonCode

Achten Sie darauf, Verwenden Sie die gleiche Grenze

0

Kolben-oauthlib.contrib enthält einen Parameter mit dem Namen auto_refresh_url/refresh_token_url in der Re mote_app, das genau das tut, was du machen wolltest. An example wie es zu benutzen wie folgt aussieht:

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

Allerdings schaffe ich nicht, um es auf diese Weise läuft. Dies ist jedoch ohne das contrib-Paket möglich. Meine Lösung bestand darin, 401-API-Aufrufe abzufangen und auf eine Aktualisierungsseite umzuleiten, wenn ein refresh_token verfügbar ist. Mein Code für den Refresh-Endpunkt sieht wie folgt aus:

@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('/') 

Hope this helfen. Der Code kann natürlich verbessert werden und es gibt sicherlich einen eleganteren Weg als 401er zu fangen, aber es ist ein Anfang;)

Eine andere Sache: Speichern Sie die Tokens nicht im Flask Session Cookie. Verwenden Sie lieber Server Side Sessions von "Flask Session", die ich in meinem Code gemacht habe!

Verwandte Themen