Python - Google OAuth 从授权码生成令牌

Python - Google OAuth generate token from authorization code

我有一个 python google 云函数,它接收 OAuth 授权代码作为参数。我想将此代码交换为可用于验证服务对象的令牌。

代码在外部生成并作为字符串参数传递给此函数。

我查看了 google_auth_oauthlib.flow 的文档。但它需要创建一个流对象来处理身份验证。在我的例子中,我只有代码作为结果。

如何将授权代码作为字符串交换为令牌?

除了授权码,您还需要更多信息。 Google 关于如何将授权码交换为访问令牌的文档位于:Exchange authorization code for refresh and access tokens.

具体来说,除了code,您还需要:

  • client_id:从API控制台[凭据页面]获得的客户端ID |(https://console.developers.google.com/apis/credentials).
  • client_secret: 从API控制台获取的客户端密码Credentials page.
  • grant_type: authorization_code
  • redirect_uri:初始授权请求中使用的重定向URI。如果这是用于 CLI(或类似的),则可能是 urn:ietf:wg:oauth:2.0:oob(对于 out-of-band)

其中大部分(client_idclient_secretgrant_type)都是静态的,因此您可以将它们用作云函数中的配置。如果您确定生成 code 的流程,redirect_uri 可能是静态的。

有了这些信息,您应该能够在您的链接示例中创建 Flow 对象并获取令牌。

作为将所有这些配置存储在您的云函数中的替代方法,您可以使用托管 OAuth 服务,例如 Xkit我工作的地方),它处理授权过程,让您只需一个 API 密钥即可从任何地方(包括云功能)检索访问令牌。

我最近 运行 自己遇到了这个问题,试图访问 AdSense API。 Google 的文档非常稀疏,这无济于事,出于某种奇怪的原因使用 Flask,这意味着您必须检索 authorization_response 而不是实际授权 code,并引用 a few different non-working Python 示例,似乎是为早已弃用的 Python 1.4.

而写的

但是,根据他们的示例和一些实现了一些最新修复的博客文章(但在我尝试时仍然出现问题),我设法拼凑了一些工作代码。

我的文件 utils.py,我在其中定义 initialize_service 以初始化我与 AdSense 的连接 API:

"""
Auxiliary file for AdSense Management API code samples.
Handles various tasks to do with logging, authentication and initialization.
"""

import os

from apiclient.discovery import build

from oauth2client.client import OAuth2Credentials
from oauth2client.file import Storage
from googleapiclient.http import build_http

import google_auth_oauthlib.flow

MY_DOMAIN = '<your domain here>'

def initialize_service():
    """Builds instance of service from discovery data and does auth."""

    client_secrets = os.path.join(os.path.dirname(__file__), 'client_secrets.json')

    flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
        client_secrets, scopes=['https://www.googleapis.com/auth/adsense.readonly'])
    flow.redirect_uri = f'https://{MY_DOMAIN}/oauth2callback'

    # If the credentials don't exist or are invalid run through the native client
    # flow. The Storage object will ensure that if successful the good
    # Credentials will get written back to a file.
    storage = Storage('adsense.dat')
    credentials = storage.get()
    if credentials is None or credentials.invalid:

        auth_url, _ = flow.authorization_url(prompt='consent')
        print('Log into the Google Account you use to access your AdWords account ' \
         'and go to the following URL: \n%s\n' % auth_url)
        print('After approving the token enter the verification code (if specified).')
        code = input('Code:').strip()

        flow.fetch_token(code=code)
        print('Access token: %s' % flow.credentials.token)
        print('Refresh token: %s' % flow.credentials.refresh_token)

        # Flow creates a google.oauth2.credentials.Credentials instance but storage
        # can only save and load a oauth2client.client.Credentials
        # instance, so we have to convert it.
        old_creds = flow.credentials
        good_creds = OAuth2Credentials(
            access_token=old_creds.token,
            client_id=old_creds.client_id,
            client_secret=old_creds.client_secret,
            refresh_token=old_creds.refresh_token,
            token_expiry=old_creds.expiry,
            token_uri=old_creds.token_uri,
            user_agent='my-agent',
            id_token=old_creds.id_token,
            scopes=old_creds.scopes,
        )
        storage.put(good_creds)
        credentials = storage.get()

    http = credentials.authorize(http=build_http())

    service = build("adsense", "v1.4", http=http)

    return service

这是应该回答您问题的代码,因为我获得了授权代码,并调用 flow.fetch_token(code=code) 将其转换为令牌,然后我将其存储在文件中以备将来重新使用 adsense.dat.

我 运行 遇到的问题是来自多个包的多个 类 用于存储 OAuth 凭据,它们都被混淆地命名为同一事物,但它们有点不兼容。 flow.fetch_token() 函数在内部将其凭据存储为 google.oauth2.credentials.Credentials 实例,但用于存储和加载这些凭据的代码仅接受 oauth2client.client.Credentials 实例,因此我不得不编写一些代码来从一个实例转换给另一个。

然后,要调用 API,您的代码如下:

from utils import initialize_service

service = initialize_service()

result = service.reports().generate(
    startDate='<start date>',
    endDate='<end date>',
    filter=['AD_CLIENT_ID==<your ad client id>'],
    metric=[
        'PAGE_VIEWS',
        'EARNINGS'
    ],
    dimension=[
        'AD_UNIT_NAME',
    ],
).execute()