You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Heroku部署Gmail API服务端应用:无需input()获取OAuth2授权码

解决Heroku上Google OAuth2授权无需input()获取授权码的问题

你当前的代码是桌面应用的OAuth授权流程,依赖交互式终端输入授权码,但Heroku是无头(无终端交互)的服务端环境,所以input()会触发EOFError。服务端Web应用需要改用基于回调的OAuth2授权流程,以下是具体解决方案:

核心思路调整

  • 把Google OAuth客户端类型从「桌面应用」改成「Web应用」,配置自己的回调地址(不能用developers.google.com/oauthplayground)。
  • 用Web路由处理授权请求和回调,自动获取授权码,无需手动输入。
  • 避免用本地文件保存凭证(Heroku临时文件系统重启会丢失),改用环境变量或数据库持久化。

完整代码示例(基于Flask,Heroku常用Web框架)

from flask import Flask, redirect, request, session
import google_auth_oauthlib.flow
import pickle
import os
import base64
import json
from googleapiclient.discovery import build
from google.auth.transport.requests import Request

# 初始化Flask应用
app = Flask(__name__)
app.secret_key = os.environ.get('FLASK_SECRET_KEY')  # 从Heroku环境变量设置随机密钥

# 配置参数
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']  # 替换为你的权限范围
REDIRECT_URI = 'https://your-heroku-app.herokuapp.com/oauth2callback'  # 替换为你的Heroku应用回调地址
CLIENT_SECRETS = json.loads(os.environ.get('GOOGLE_APPLICATION_CREDENTIALS'))

def load_or_refresh_credentials():
    creds = None
    # 从环境变量加载已保存的凭证(Base64编码避免格式问题)
    saved_creds = os.environ.get('SAVED_GOOGLE_CREDS')
    if saved_creds:
        creds = pickle.loads(base64.b64decode(saved_creds))

    # 刷新过期凭证或重新授权
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
            # 刷新后重新保存凭证
            updated_creds = base64.b64encode(pickle.dumps(creds)).decode('utf-8')
            # 提示:Heroku环境变量需通过CLI或控制面板持久化,这里仅内存修改示例
            os.environ['SAVED_GOOGLE_CREDS'] = updated_creds
    return creds

# 授权入口路由
@app.route('/authorize')
def start_authorization():
    flow = google_auth_oauthlib.flow.Flow.from_client_config(
        CLIENT_SECRETS, scopes=SCOPES)
    flow.redirect_uri = REDIRECT_URI

    # 生成授权URL和state(用于CSRF验证)
    authorization_url, state = flow.authorization_url(
        access_type='offline',  # 申请离线访问权限,获取刷新令牌
        include_granted_scopes='true')
    
    session['state'] = state
    return redirect(authorization_url)

# OAuth回调路由,自动获取授权码
@app.route('/oauth2callback')
def handle_oauth_callback():
    # 验证state防止跨站攻击
    session_state = session.get('state')
    if request.args.get('state') != session_state:
        return '无效的state参数', 400

    # 初始化Flow并传入state
    flow = google_auth_oauthlib.flow.Flow.from_client_config(
        CLIENT_SECRETS, scopes=SCOPES, state=session_state)
    flow.redirect_uri = REDIRECT_URI

    # 用请求中的code获取令牌(替代原来的input手动输入)
    flow.fetch_token(code=request.args.get('code'))
    creds = flow.credentials

    # 序列化凭证并保存到环境变量
    serialized_creds = base64.b64encode(pickle.dumps(creds)).decode('utf-8')
    os.environ['SAVED_GOOGLE_CREDS'] = serialized_creds

    return '授权成功!请关闭此页面。'

# 业务逻辑示例:调用Gmail API
@app.route('/get-gmail')
def fetch_gmail_data():
    creds = load_or_refresh_credentials()
    if not creds:
        # 无凭证时跳转授权
        return redirect('/authorize')
    
    service = build('gmail', 'v1', credentials=creds)
    results = service.users().messages().list(userId='me').execute()
    messages_count = len(results.get('messages', []))
    return f'已获取到{messages_count}封邮件'

if __name__ == '__main__':
    # 适配Heroku端口
    app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 5000)))

关键说明

  1. flow.fetch_token的正确用法:
    在Web流程中,不需要传入authorization_response(那是桌面应用的完整回调URL),而是直接传code=request.args.get('code'),这个code是Google重定向到回调地址时自动携带的参数。

  2. Heroku部署注意事项:

    • 在Google Cloud控制台创建Web应用类型的OAuth客户端ID,添加你的REDIRECT_URI到授权回调地址列表。
    • 在Heroku控制面板设置环境变量:
      • GOOGLE_APPLICATION_CREDENTIALS:Google客户端JSON的字符串版本。
      • FLASK_SECRET_KEY:随机生成的字符串(用于Flask session加密)。
      • 首次授权后,SAVED_GOOGLE_CREDS会被生成,可通过heroku config:set SAVED_GOOGLE_CREDS=xxx持久化。
  3. 凭证持久化:
    Heroku的本地文件系统是临时的,所以不能用token.pickle保存凭证,改用Base64编码后存在环境变量,或用数据库(如Heroku Postgres)存储更可靠。

内容的提问来源于stack exchange,提问作者Shivam Singh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 12:10:56