用于谷歌 API 访问的 Oauth2 似乎依赖于请求 Python 任务的用户 ID

Posted

技术标签:

【中文标题】用于谷歌 API 访问的 Oauth2 似乎依赖于请求 Python 任务的用户 ID【英文标题】:Oauth2 for google API access seems to be dependent on the user-ID of the requesting Python task 【发布时间】:2021-10-01 16:04:04 【问题描述】:

我正在重新编写一个需要能够发送和接收电子邮件的程序。旧版本在桌面上独立运行,并使用 SMTP 和 POP3 使用 gmail 帐户代表我发送和接收。没关系,但是最近,很多发送的电子邮件最终都进入了垃圾文件夹。所以,我想要一种更可靠的方式来“代表”用户(我)发送。新版本是一个网络应用程序而不是桌面程序,因此电子邮件活动是在服务器端完成的,我正在尝试使用 Gmail API。

当进程在服务器上运行时,我被困了几天,无法获取 API 访问令牌(“无效令牌”)。这是我所做的:

    使用 Google Cloud Platform 控制台创建/下载 credentials.json 文件 使用“快速启动”python代码成功测试用户授权,将令牌保存为文件token.json并发送测试消息 将类似的代码合并到我的程序中,但从命令行将其作为测试运行 > 成功! 将整个文件夹复制到我的服务器,并使用 SSH 终端从命令行再次运行它作为测试 > 成功(包括使用刷新令牌)! 将其作为 Web 应用程序 (WSGI Python) 运行,但它失败了。

注意到当它作为网络应用程序运行时任务的所有者不是我,在 SSH 终端上,我将用户更改为“www-data”并重新尝试测试并遇到错误(cred.valid = = 假)之后

creds = Credentials.from_authorized_user_file('token.json', scopes=SCOPES) 结果触发了用户身份验证流程 - 因为它无法远程使用(我相信),所以它停止了。

我已确保两个用户(www-data 和 ben)都可以平等地访问文件夹中的所有文件。谁能给我一个线索,我必须做些什么才能使它与“www-data”用户一起工作?

def get_credentials():

    # If modifying these scopes, delete the file token.json.
    SCOPES = ['https://www.googleapis.com/auth/gmail.readonly','https://www.googleapis.com/auth/gmail.send']

    creds = None
    # The file token.json stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    logging("In get_credentials. Python version: " + sys.version )
    if os.path.exists('token.json'):
        logging("Using token.json")
        with open('token.json',"r",encoding="utf-8") as fi:
            s = fi.read()
            logging( s )
        logging( str(os.environ))
        creds = Credentials.from_authorized_user_file('token.json', scopes=SCOPES)
        logging("Creds " + str(creds))

        logging(str(creds.refresh_token) + " " + str( creds.expired) + " " + str( creds.valid))
        
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        logging("No valid credentials found")
        if creds and creds.expired and creds.refresh_token:
            logging("Going for a refresh")
            creds.refresh(Request())
        else:
            logging("Going for live authentication flow!")
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open('token.json', 'w') as token:
            token.write(creds.to_json())
            logging("Saved token.json")

    service = build('gmail', 'v1', credentials=creds)


    return service

【问题讨论】:

【参考方案1】:

您遇到的问题是您使用的是 InstalledAppFlow。这将导致在运行代码的机器上弹出同意屏幕,然后将刷新令牌和访问令牌存储在 token.json 中。

您可以做的是在本地运行一次以确保创建了 token.json,然后确保在将其上传到服务器时也上传了 token.json。

请记住,除非您的应用程序设置为生产,否则您的刷新令牌只能使用 7 天,因此您需要每周在本地重新授权一次。

【讨论】:

感谢您的快速回答!我确实在本地运行它来创建 token.json 并在上传到服务器时包含该文件。这就是它作为服务器上的命令行测试运行良好的方式。我不认为我可以将申请批准为“生产”,因此 7 天的限制将是一个问题。有更好的方法吗? 测试中的应用程序没有刷新令牌,仅持续 7 天。然后你需要再次授权。

以上是关于用于谷歌 API 访问的 Oauth2 似乎依赖于请求 Python 任务的用户 ID的主要内容,如果未能解决你的问题,请参考以下文章

重复使用 Oauth2 凭据来访问 YouTube api [关闭]

oauth2.0授予Web应用访问用户的Google spreadhseets所需的最小范围是多少?

使用 OAuth2 客户端凭据流保护用 PHP 编写的 REST API

谷歌 oauth2 中的 invalid_client

Passport.js Google 策略不适用于 React 应用程序

通过 Oauth2.0 向 Google Latitude API 发送用户新位置