如何使用oauth2并使用google api刷新令牌? [英] How do I use oauth2 and refresh tokens with the google api?

查看:170
本文介绍了如何使用oauth2并使用google api刷新令牌?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我只是花了几天的时间来解决这个问题,并提出这个问题,以便我可以为其他有问题的人回答。



首先,谷歌文档是可怕的,并使用不同的oauth2库,这取决于您正在查看的许多google API示例中的哪一个。它通常是自相矛盾的,有时直接有代码不起作用。



哦。



所以我的问题基本上是:


  1. 如何使用google api库让我的用户授予我访问其Google帐户的权限?

  2. 如何存储google返回的oauth2访问令牌,以便我可以在几天后使用它们?

  3. 我如何实际使用refresh_token并刷新它?

请参阅下面的答案以获取全功能授权流程,从获取初始令牌到保存它,后来,刷新它,并使用它。



干杯。

解决方案

首先,关于如何使用他们的API的谷歌文档是可怕的和自相矛盾的。

继承我的解决方案(使用他们的库)使用oauth2来使用令牌存储在数据库中并定期刷新。我使用的是Django 2.0和python 3.6。这一切都在我的'views.py'文件中。



首先,导入和其他脚本范围的设置:

 进口google.oauth2.credentials 
进口google.auth.transport.requests
从googleapiclient.discovery进口google_auth_oauthlib.flow
进口生成

import os
import json
import datetime
$ b API_SCOPE = ['https://mail.google.com/',]
JSON_FILE =test_server_client_json.json
JSON_PATH = os.path.join(os.getcwd(),< folder_name>,JSON_FILE)
如果settings.TEST_SERVER:
REDIRECT_URI =http :// localhost:5000 / oauth2 / gmail /
#我们在本地主机上没有ssl
os.environ ['OAUTHLIB_INSECURE_TRANSPORT'] ='1'
else:
REDIRECT_URL =https://www.example.com/oauth2/gmail/

好的,这里是我们发送用户启动auth进程的第一个服务器端点/页面。

  @login_required 
def conne ct_gmail_to_manager_page_1(请求):
#this是新用户用于设置其gmail帐户
#并将其连接到我们的系统的功能。
#此特定页面用于:
#1)让用户输入他们的电子邮件地址,以便我们知道
#2发生了什么)解释过程
#== ===================
#基本上我们可以在此页面上获得他们的电子邮件地址,就是这样。然后我们将它们
#发送给Google,以授予我们访问权限。
if request.method ==POST:
form = admin.getEmailAddress(request.POST)
if form.is_valid():
#first,获取他们的电子邮件地址。这是可选的。
#我使用django和他们的形式来获得它。
new_email = form.cleaned_data.get(email)
#-----
#我们将使用< redacted>的键和这样的$来创建流对象b $ b flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
JSON_PATH,
scopes = API_SCOPE)

flow.redirect_uri = REDIRECT_URI

#生成向Google的OAuth 2.0服务器发送请求的网址。
#使用kwargs设置可选的请求参数。
authorization_url,state = flow.authorization_url(
#启用离线访问,以便刷新访问令牌而不用
#重新提示用户提供权限。 $ b access_type ='offline',
#which电子邮件正在尝试登录
login_hint = new_email,
#启用增量授权,建议作为最佳做法
include_granted_scopes =' true')

#最后,我们将它们发送给谷歌,以便他们提供访问:
return HttpResponseRedirect(authorization_url)
else:
form = admin。 getEmailAddress()
token = {}
token.update(csrf(request))
token ['form'] = form
return render(request,'connect_gmail_to_manager_page_1.html' ,令牌)

这个s结束用户关闭谷歌给我们授权。授权后,用户将被重定向到我们服务器上的授权端点。这里是我的授权端点(我在这里删除了一些特定于项目的代码)

  @login_required 
def g_auth_endpoint请求):
#this是登录令牌发送到
#的端点,我们基本上将gmail提供的授权代码交换为访问令牌。
#访问令牌允许我们发送电子邮件。
#it是一个直通终点:我们希望重定向到
#的下一个阶段,无论他们在此处完成的任何过程如何。
#============================================= ==
#首先我们需要从url
获得参数'状态'注意,你应该在这里做一些错误处理,因为它不是一个有效的标记。我已经删除了堆栈溢出的简洁性
state = request.GET.get('state',None)

flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
JSON_PATH,
scopes = API_SCOPE,
state = state)
flow.redirect_uri = REDIRECT_URI

#获取我们所在的完整URL,包括所有的? param1 = token& param2 = key的参数,谷歌发送给我们。
authorization_response = request.build_absolute_uri()

#把这些参数变成一个令牌。
flow.fetch_token(authorization_response = authorization_response)

credentials = flow.credentials

#现在我们构建API服务对象
service = build(' gmail','v1',credentials = credentials)
#ok。真棒!
#他们使用哪些电子邮件? (这只是一个如何使用api的例子 - 如果你愿意,你可以跳过这部分)
profile = service.users()。getProfile(userId =me)。execute()
email_address = profile ['emailAddress']
#ok。现在我们得到活动管理器
manager = get_active_manager(request.user)
#<大量项目特定的代码被移除>
#注意:'manager'对象是项目特定类型的对象。
#I存储身份验证令牌。
#alright,如果我们到了这里,我们有一个有效的管理对象。

#现在可以创建/更新数据库中的凭证对象。
temp = save_credentials(经理,凭证)
#现在发送他们的快乐方式,你有权访问
return HttpResponse(http://www.example.com)

这里是我正在使用的保存/加载功能。请注意'manager'和'Gmail_Connection_Token'对象是我保存令牌的项目特定对象。

  def save_credentials( manager,credentials,valid = True):
#这是应该调用的函数来保存各种令牌。
#credentials是一个google.oauth2.credentials.Credentials()对象。
#this以易于将
#与Load_credentials(manager)中的同一类型对象相互转换的格式将其保存。
#valid在大多数情况下始终为真,但如果由于某种原因不是
#则一定要设置该标志。
#this以字典形式返回凭证(忽略有效标志)
#--------------------------- ------------
#首先我们得到或创建正确的DB对象
尝试:
creds = Gmail_Connection_Token.objects.get(manager = manager)
,除了Gmail_Connection_Token.DoesNotExist:
creds = Gmail_Connection_Token()
creds.manager =管理员
#现在我们将传入凭证obj转换为一个字典obj
#note到期格式化
temp = {
'token':credentials.token,
'refresh_token':credentials.refresh_token,$ b $'id_token':credentials.id_token,
'token_uri ':credentials.token_uri,$ b $'client_id':credentials.client_id,
'client_secret':credentials.client_secret,$ b $'scopes':credentials.scopes,
'expiry': datetime.datetime.strftime(credentials.expiry,'%Y-%m-%d%H:%M:%S')
}
#现在我们把它作为json_string保存到creds DB中obj
creds.json_string = json.dumps(temp)
#update有效标志。
creds.valid = valid
#and将每一个元素保存在DB
creds.save()
#中,最后返回刚刚创建的字典。
return temp

和继承人如何在需要时加载令牌:

  def load_credentials(manager,ignore_valid = False):
#this是应该调用以加载凭证对象的函数数据库。
#it加载,刷新并返回一个google.oauth2.credentials.Credentials()对象。
#如果valid = False,则引发错误值
#------
#NOTE:if'ignore_valid'为真:
#will不会引发值错误if valid == False
#返回格式为(Credentails(),valid_boolean)的元组
#======================== ==============
尝试:
creds = Gmail_Connection_Token.objects.get(经理=经理)
除外:
#如果有事情发生在这里错了,我们只想提出错误
#并将其传递给调用函数。
提升#是,这是正确的语法! (不想丢失堆栈跟踪)
#有效吗?我们是否提出错误?
如果不是ignore_valid而不是creds.valid:
raise ValueError('凭证无效')
#ok,如果我们到这里我们加载/创建证书obj()
temp = json.loads(creds.json_string)
credentials = google.oauth2.credentials.Credentials(
temp ['token'],
refresh_token = temp ['refresh_token'] ,
id_token = temp ['id_token'],
token_uri = temp ['token_uri'],
client_id = temp ['client_id'],
client_secret = temp ['client_secret '],
scopes = temp ['scopes'],

expiry = temp ['expiry']
expiry_datetime = datetime.datetime.strptime(expiry,'%Y - %m-%d%H:%M:%S')
credentials.expiry = expiry_datetime
#现在我们刷新令牌
#但是如果我们知道它不是一个有效的令牌。
if creds.valid:
request = google.auth.transport.requests.Request()
if credentials.expired:
credentials.refresh(request)
#最后,如果ignore_valid:
return(凭证,creds.valid)
else:
返回凭证

这几乎就是一切。这里是一个示例端点,展示了如何在需要访问Gmail API时使用这些功能。

  @login_required 
def test_endpoint(request):
#获取我们用于存储令牌的项目特定管理器数据库对象
manager = get_active_manager(request.user)
#并将该管理器对象转换为google凭证对象
凭证= load_credentials(经理)

#无论你需要什么gmail api在这里:
msg = send_test_email(凭证)

#完成后,请务必保存/更新数据库中的凭证以备将来使用。
save_credentials(经理,凭证)

#然后以快乐的方式发送您的用户。
return HttpResponse(msg)


So I just spent the last few days trying to figure this out and am asking this question so that I can answer it for other people who are having problems.

First, the google documentation is TERRIBLE and uses different oauth2 libraries depending on which of the many google API examples you are looking at. It is often self-contradictory and sometimes straight up has code in it that doesn't work.

Oh well.

so my questions were basically:

  1. how do I use the google api libraries to have my users grant me access to their google accounts?
  2. how do I store the oauth2 access tokens that google returns so that I can use them a few days from now?
  3. how do i actually use the refresh_token and refresh it?

see answer below for a fully functional authorization flow, from getting the initial token to saving it, loading it later, refreshing it, and using it.

Cheers.

解决方案

first, the google docs on how to use their API are terrible and self-contradictory.

heres my solution (using their libraries) to using oauth2 to using tokens that I store in a database and refresh periodically. I'm using django 2.0 and python 3.6. this is all in my 'views.py' file.

first, the imports and other script-wide setttings:

import google.oauth2.credentials
import google.auth.transport.requests
import google_auth_oauthlib.flow
from googleapiclient.discovery import build

import os
import json
import datetime

API_SCOPE = ['https://mail.google.com/',]
JSON_FILE = "test_server_client_json.json"
JSON_PATH = os.path.join(os.getcwd(),"<folder_name>",JSON_FILE)
if settings.TEST_SERVER:
    REDIRECT_URI = "http://localhost:5000/oauth2/gmail/"
    #we don't have ssl on local host
    os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
else:
    REDIRECT_URL = "https://www.example.com/oauth2/gmail/"

Ok, and here is the first server endpoint/page that we send the user to start the auth process

@login_required
def connect_gmail_to_manager_page_1(request):
    #this is the function that a new user uses to set up their gmail account
    #and connect it to our system.
    #this particular page is used to:
    #1) have the user enter their email address so we know what is going on
    #2) explain the process
    #=====================    
    #basically we get their email address, and thats it, on this page. then     we send them 
    #to google to grant us access.
    if request.method == "POST":
        form = admin.getEmailAddress(request.POST)
        if form.is_valid():
            #first, get their email address. this is optional.
            #i'm using django and their forms to get it.
            new_email = form.cleaned_data.get("email")
            #-----
            #we are going to create the flow object using <redacted>'s keys and such
            flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
                JSON_PATH,
                scopes=API_SCOPE)

            flow.redirect_uri = REDIRECT_URI

            # Generate URL for request to Google's OAuth 2.0 server.
            # Use kwargs to set optional request parameters.
            authorization_url, state = flow.authorization_url(
                # Enable offline access so that you can refresh an access     token without
                # re-prompting the user for permission. Recommended for web     server apps.
                access_type='offline',
                #which email is trying to login?
                login_hint=new_email,
                # Enable incremental authorization. Recommended as a         best     practice.
                include_granted_scopes='true')

            #and finally, we send them off to google for them to provide     access:
            return HttpResponseRedirect(authorization_url)
    else:    
        form = admin.getEmailAddress()
    token = {}
    token.update(csrf(request))
    token['form'] = form
    return render(request,'connect_gmail_to_manager_page_1.html',token)

This sends the user off to google to give us authorization. after they grant it, the user is redirected to an authorization endpoint on our servers. Here is my authorization endpoint (i've removed some project-specific code here)

@login_required
def g_auth_endpoint(request):
    #this is the endpoint that the logged in token is sent to
    #here we are basically exchanging the auth code provided by gmail for an     access token.
    #the access token allows us to send emails.
    #it is a passthrough endpoint: we want to redirect to the next stage of 
    #whatever process they are doing here on completion.
    #===============================================
    #first we need to get the paramater 'state' from the url
    #NOTE that you should do some error handling here incase its not a valid token. I've removed that for brevity on stack overflow
    state = request.GET.get('state',None)

    flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
        JSON_PATH,
        scopes=API_SCOPE,
        state=state)
    flow.redirect_uri = REDIRECT_URI

    #get the full URL that we are on, including all the "?param1=token&param2=key" parameters that google has sent us.
    authorization_response = request.build_absolute_uri()        

    #now turn those parameters into a token.
    flow.fetch_token(authorization_response=authorization_response)

    credentials = flow.credentials

    #now  we build the API service object    
    service = build('gmail', 'v1', credentials=credentials)
    #ok. awesome!
    #what email did they use? (this is just an example of how to use the api - you can skip this part if you want)
    profile = service.users().getProfile(userId="me").execute()
    email_address = profile['emailAddress']
    #ok. now we get the active manager
    manager = get_active_manager(request.user)
    #<lots of project specific code removed>
    #NOTE: 'manager' object is a project-specific type of object.
    #I store the auth token in it.
    #alright, if we get to here we have a valid manager object.

    #now lets create/update the credentials object in the DB.
    temp = save_credentials(manager,credentials)
    #now send them on their merry way that you've got access
    return HttpResponse("http://www.example.com")

here is the save/load functions i'm using. note that the 'manager' and 'Gmail_Connection_Token' objects are project specific objects that I'm saving tokens in.

def save_credentials(manager,credentials,valid=True):
    #this is the function that should be called to save the various tokens.
    #credentials is a google.oauth2.credentials.Credentials() object.
    #this saves it in a format that is easy to turn back 
    #into the same type of object in load_credentials(manager).
    #valid is, for the most part, always going to be true, but if for some reason its not
    #make sure to set that flag.
    #this returns the credentials as a dict (ignores the valid flag)
    #---------------------------------------
    #first we get or create the correct DB object
    try:
        creds = Gmail_Connection_Token.objects.get(manager=manager)
    except Gmail_Connection_Token.DoesNotExist:
        creds = Gmail_Connection_Token()
        creds.manager = manager
    #now we turn the passed in credentials obj into a dicts obj
    #note the expiry formatting
    temp = {
        'token': credentials.token,
        'refresh_token': credentials.refresh_token,
        'id_token':credentials.id_token,
        'token_uri': credentials.token_uri,
        'client_id': credentials.client_id,
        'client_secret': credentials.client_secret,
        'scopes': credentials.scopes,
        'expiry':datetime.datetime.strftime(credentials.expiry,'%Y-%m-%d %H:%M:%S')
    }
    #now we save it as a json_string into the creds DB obj
    creds.json_string = json.dumps(temp)
    #update the valid flag.
    creds.valid = valid
    #and save everythign in the DB
    creds.save()
    #and finally, return the dict we just created.
    return temp

and heres how I load the token whenever I need it:

def load_credentials(manager,ignore_valid=False):
    #this is the function that should be called to load a credentials object     from the database.
    #it loads, refreshes, and returns a     google.oauth2.credentials.Credentials() object.
    #raises a value error if valid = False 
    #------
    #NOTE: if 'ignore_valid' is True:
    #will NOT raise a value error if valid == False
    #returns a Tuple formated as (Credentails(),valid_boolean)
    #======================================
    try:
        creds = Gmail_Connection_Token.objects.get(manager=manager)
    except: 
        #if something goes wrong here, we want to just raise the error
        #and pass it to the calling function.
        raise #yes, this is proper syntax! (don't want to lose the stack     trace)
    #is it valid? do we raise an error?
    if not ignore_valid and not creds.valid:
        raise ValueError('Credentials are not valid.')
    #ok, if we get to here we load/create the Credentials obj()
    temp = json.loads(creds.json_string)
    credentials = google.oauth2.credentials.Credentials(
        temp['token'],
        refresh_token=temp['refresh_token'],
        id_token=temp['id_token'],
        token_uri=temp['token_uri'],
        client_id=temp['client_id'],
        client_secret=temp['client_secret'],
        scopes=temp['scopes'],
    )
    expiry = temp['expiry']
    expiry_datetime = datetime.datetime.strptime(expiry,'%Y-%m-%d %H:%M:%S')
    credentials.expiry = expiry_datetime
    #and now we refresh the token   
    #but not if we know that its not a valid token.
    if creds.valid:
        request = google.auth.transport.requests.Request()
        if credentials.expired:
            credentials.refresh(request)
    #and finally, we return this whole deal
    if ignore_valid:
        return (credentials,creds.valid)
    else:
        return credentials

thats pretty much everything. here is an example endpoint that shows how to use these functions whenever you need to access the Gmail api

@login_required
def test_endpoint(request):
    #get the project-specific manager database object we are using to store the tokens
    manager = get_active_manager(request.user)
    #and convert that manager object into the google credentials object
    credentials = load_credentials(manager)

    #do whatever you need the gmail api for here:
    msg = send_test_email(credentials)

    #when you're done, make sure to save/update the credentials in the DB for future use.
    save_credentials(manager,credentials)

    #then send your user on their merry way.
    return HttpResponse(msg)

这篇关于如何使用oauth2并使用google api刷新令牌?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆