Python Google API:使用 oauth2client.client 从刷新令牌中获取凭据

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27771324/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 02:14:37  来源:igfitidea点击:

Google API: getting Credentials from refresh token with oauth2client.client

pythongoogle-apigoogle-plus

提问by bjelli

I am using googles official oauth2client.client to access the google plus api. I have a refresh token (that does not expire) stored in a database, and need to recreate the temporary "Credentials" (access token) from that.

我正在使用 googles 官方 oauth2client.client 来访问 google plus api。我有一个存储在数据库中的刷新令牌(不会过期),并且需要从中重新创建临时“凭据”(访问令牌)。

But I could not find a way to do this with to official library supplied by google.

但是我找不到一种方法来使用谷歌提供的官方库来做到这一点。

So I hacked around it: used urllib to access the API that gives me a new access_token from the refresh_token. Using the access_token I can then use the library.

所以我绕过了它:使用 urllib 访问 API,该 API 为我提供了来自 refresh_token 的新 access_token。使用 access_token 然后我可以使用该库。

I must be missing somthing!

我一定是错过了什么!

from apiclient import discovery
from oauth2client.client import AccessTokenCredentials
from urllib import urlencode
from urllib2 import Request , urlopen, HTTPError
import json

# ==========================================

def access_token_from_refresh_token(client_id, client_secret, refresh_token):
  request = Request('https://accounts.google.com/o/oauth2/token',
    data=urlencode({
      'grant_type':    'refresh_token',
      'client_id':     client_id,
      'client_secret': client_secret,
      'refresh_token': refresh_token
    }),
    headers={
      'Content-Type': 'application/x-www-form-urlencoded',
      'Accept': 'application/json'
    }
  )
  response = json.load(urlopen(request))
  return response['access_token']

# ==========================================

access_token = access_token_from_refresh_token(CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN)

# now I can use the library properly
credentials = AccessTokenCredentials(access_token, "MyAgent/1.0", None)
http = credentials.authorize(httplib2.Http())
service = discovery.build('plus', 'v1', http=http)
google_request = service.people().get(userId='me')
result = google_request.execute(http=http)

回答by tjsar

You could store the entire credentials rather than only the refresh token:

您可以存储整个凭据,而不仅仅是刷新令牌:

json = credentials.to_json()
credentials = Credentials.new_from_json(json)

Look at the Storage objectwhich does it this way.

查看以这种方式执行此操作的Storage 对象

回答by swdev

I solved this quite easily (you certainly miss this documentation). This is a snippet of my code that tries to use Picasa API to get all of album from active user:

我很容易地解决了这个问题(你肯定会错过这个文档)。这是我尝试使用 Picasa API 从活动用户获取所有相册的代码片段:

    http = httplib2.Http(ca_certs=os.environ['REQUESTS_CA_BUNDLE'])
    try:
        http = self.oauth.credentials.authorize(http)
        response, album_list = http.request(Picasa.PHOTOS_URL, 'GET')
        if response['status'] == '403':
            self.oauth.credentials.refresh(http)
            response, album_list = http.request(Picasa.PHOTOS_URL, 'GET')
        album_list = json.load(StringIO(album_list))
    except Exception as ex:
        Logger.debug('Picasa: error %s' % ex)
        return {}

Use the refreshmethod coming from oauth2client.client.OAuth2Credentials. I think it's even okay to use if response['status'] != '200'. Got to check that!

使用refresh来自oauth2client.client.OAuth2Credentials的方法。我认为甚至可以使用if response['status'] != '200'. 必须检查一下!

回答by leah

I use: oauth2client.client.GoogleCredentials

我使用:oauth2client.client.GoogleCredentials

    cred = oauth2client.client.GoogleCredentials(access_token,client_id,client_secret,
                                          refresh_token,expires_at,"https://accounts.google.com/o/oauth2/token",some_user_agent)
    http = cred.authorize(httplib2.Http())
    cred.refresh(http)
    self.gmail_service = discovery.build('gmail', 'v1', credentials=cred)

回答by Eugene Yarmash

You can construct an OAuth2Credentialsinstance directly like this:

你可以OAuth2Credentials像这样直接构造一个实例:

import httplib2
from oauth2client import GOOGLE_REVOKE_URI, GOOGLE_TOKEN_URI, client

CLIENT_ID = '<client_id>'
CLIENT_SECRET = '<client_secret>'
REFRESH_TOKEN = '<refresh_token>'

credentials = client.OAuth2Credentials(
    access_token=None,  # set access_token to None since we use a refresh token
    client_id=CLIENT_ID,
    client_secret=CLIENT_SECRET,
    refresh_token=REFRESH_TOKEN,
    token_expiry=None,
    token_uri=GOOGLE_TOKEN_URI,
    user_agent=None,
    revoke_uri=GOOGLE_REVOKE_URI)

credentials.refresh(httplib2.Http())  # refresh the access token (optional)
print(credentials.to_json())
http = credentials.authorize(httplib2.Http())  # apply the credentials

回答by 599644

Wow.. 2 years old question and not a good answer.. No surprise given that Google documentation is crap regarding this.

哇.. 2 岁的问题,而不是一个好的答案.. 考虑到谷歌文档在这方面是废话也就不足为奇了。

The correct way to do this is by extending the Storage class oauth2client.client.Storage

正确的方法是扩展 Storage 类 oauth2client.client.Storage

An example implementation(using mongodbcollection _google_credentials) would be something like:

一个示例实现(使用mongodbcollection _google_credentials)类似于:

class Storage(oauth2client.client.Storage):

def __init__(self, key):
    super(Storage, self).__init__()
    self._key = key

def locked_get(self):
    if not self._key: return None
    data = _google_credentials.find_one({'_id': self._key})
    if not data: return None
    credentials = oauth2client.client.Credentials.new_from_json(json.dumps(data))
    credentials.set_store(self)
    return credentials

def locked_put(self, credentials):
    data = json.loads(credentials.to_json())
    _google_credentials.update_one({'_id': self._key}, {'$set': data}, 
        upsert=True)
    credentials.set_store(self)

def locked_delete(self):
    bucket.delete(self._key)

Then when you initially get the credentials after step2_exchange, you need to store them using Storage().put:

然后,当您最初获得凭证后step2_exchange,您需要使用Storage().put以下方式存储它们:

e.g:

例如:

credentials = flow.step2_exchange(code)
Storage(user_id).put(credentials)

When you need the credentials again, just do:

当您再次需要凭据时,只需执行以下操作:

credentials = Storage(user_id).get()

回答by KiHyun Nam

I recommend this method.

我推荐这种方法。

from oauth2client import client, GOOGLE_TOKEN_URI

CLIENT_ID = "client_id"
CLIENT_SECRET = "client_secret"
REFRESH_TOKEN = "refresh_token"


credentials = client.OAuth2Credentials(
    access_token = None, 
    client_id = CLIENT_ID, 
    client_secret = CLIENT_SECRET, 
    refresh_token = REFRESH_TOKEN, 
    token_expiry = None, 
    token_uri = GOOGLE_TOKEN_URI,
    token_ id = None, 
    revoke_uri= None)

http = credentials.authorize(httplib2.Http())

Even if the access token has expired, the credential is still authorize because of the refresh token.

即使访问令牌已过期,由于刷新令牌,凭证仍然是授权的。

回答by Ray Hulha

If you are using the 2018 Youtube Python Quickstart demo appusing google-auth, you can't use oauth2client's storage.

如果您使用 google-auth使用2018 Youtube Python Quickstart 演示应用程序,则不能使用 oauth2client 的存储。

So here is the correct way of storing the credentials

所以这是存储凭据的正确方法

Here is a partially working solution for google-auth, missing the correct handling of the case where the token expires:

这是 google-auth 的部分工作解决方案,缺少对令牌过期情况的正确处理:

import os
import json
import os.path
import google.oauth2.credentials
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from google_auth_oauthlib.flow import InstalledAppFlow

CLIENT_SECRETS_FILE = "client_secret.json"
SCOPES = ['https://www.googleapis.com/auth/youtube.force-ssl']
API_SERVICE_NAME = 'youtube'
API_VERSION = 'v3'

def get_authenticated_service():

  if os.path.isfile("credentials.json"):
    with open("credentials.json", 'r') as f:
      creds_data = json.load(f)
    creds = Credentials(creds_data['token'])

  else:
    flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRETS_FILE, SCOPES)
    creds = flow.run_console()
    creds_data = {
          'token': creds.token,
          'refresh_token': creds.refresh_token,
          'token_uri': creds.token_uri,
          'client_id': creds.client_id,
          'client_secret': creds.client_secret,
          'scopes': creds.scopes
      }
    print(creds_data)
    with open("credentials.json", 'w') as outfile:
      json.dump(creds_data, outfile)
  return build(API_SERVICE_NAME, API_VERSION, credentials = creds)

def channels_list_by_username(service, **kwargs):
  results = service.channels().list(**kwargs).execute()
  print('This channel\'s ID is %s. Its title is %s, and it has %s views.' %
       (results['items'][0]['id'],
        results['items'][0]['snippet']['title'],
        results['items'][0]['statistics']['viewCount']))

if __name__ == '__main__':
  os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
  service = get_authenticated_service()
  channels_list_by_username(service, part='snippet,contentDetails,statistics', forUsername='GoogleDevelopers')

回答by Neil

In case anyone is looking for the answer for how use a refresh token with google_auth_oauthlib, the following works for me:

如果有人正在寻找如何使用刷新令牌的答案google_auth_oauthlib,以下对我有用:

flow.oauth2session.refresh_token(flow.client_config['token_uri'],
                                 refresh_token=refresh_token,
                                 client_id=<MY_CLIENT_ID>,
                                 client_secret=flow.client_config['client_secret'])
creds = google_auth_oauthlib.helpers.credentials_from_session(
    flow.oauth2session, flow.client_config)

I cannot find anywhere where this is documented though.

不过,我找不到任何记录此内容的地方。