获取与刷新 Google 用户令牌
在自己电脑完成授权,把用户同意的权限变成后续接口可用的访问令牌。
← 返回平台功能接入准备
- Google Cloud 项目已启用准备调用的 Drive、Docs 或 Sheets API;具备配置 OAuth 的项目权限。
准备环境变量
示例值需要替换为自己的凭证或资源 ID。代码在本机或服务端运行;密钥不应放进公开网页。
GOOGLE_CLIENT_ID- 虚构示例,在 Google Auth Platform → Clients 创建自己的客户端。
GOOGLE_CLIENT_SECRET- 仅保存在自己的终端或后端,不能发到静态站点。
GOOGLE_SCOPES- 空格分隔的 scope;运行 Docs/Sheets 例子时按步骤添加对应完整范围并重新授权。
GOOGLE_REFRESH_TOKEN- 仅刷新操作需要;来自首次 offline 授权的响应。
export GOOGLE_CLIENT_ID='example-client-id.apps.googleusercontent.com'
export GOOGLE_CLIENT_SECRET='replace_with_client_secret'
export GOOGLE_SCOPES='https://www.googleapis.com/auth/drive.metadata.readonly'
export GOOGLE_REFRESH_TOKEN='replace_with_refresh_token'以上 export 适用于 macOS / Linux 的终端,在同一终端内运行后续代码。Python 示例保存为 request.py 后执行 python3 request.py。
调用步骤
- 01
进入 Google Cloud 项目 → APIs & Services → Library,按需要启用 Google Drive API、Google Docs API、Google Sheets API。
- 02
打开 Google Auth Platform:填写 Branding;在 Audience 选择适用的内部/外部用户。外部应用测试时将自己的 Google 账号加到 Test users。
- 03
在 Data Access 添加本场景所需 scope:仅列文件元数据用 https://www.googleapis.com/auth/drive.metadata.readonly;编辑 Docs 用 https://www.googleapis.com/auth/documents;读写 Sheets 用 https://www.googleapis.com/auth/spreadsheets。只申请实际需要的范围。
- 04
在 Clients 创建 Web application,Authorized redirect URIs 登记 http://localhost:8765/oauth2callback,取得 Client ID 与 Client secret。
- 05
设置 GOOGLE_CLIENT_ID、GOOGLE_CLIENT_SECRET;GOOGLE_SCOPES 以空格分隔填写需要的完整 scope。将下方 Python 代码保存并运行,在弹出的 Google 页面用测试用户确认权限。
- 06
脚本在本机保存 google-oauth-token.json;将 access_token 放入 GOOGLE_ACCESS_TOKEN,供本站其余三个场景使用。refresh_token 放入 GOOGLE_REFRESH_TOKEN,过期时执行刷新操作。
- 07
正式向其他用户发布前,完成适用的 OAuth 验证及组织策略要求;scope 不会越过文件本身的共享权限。
完成用户授权并交换令牌
Python 示例包含本地回调、state 校验和授权码交换;HTTP 表格展示交换端点,凭证由脚本从环境变量读取。
https://oauth2.googleapis.com/token请求头
Content-Typeapplication/x-www-form-urlencoded
参数说明
参数表可左右滑动,查看完整说明。
| 字段 / 位置 | 类型 / 必填 | 说明 |
|---|---|---|
client_id请求体 | string必填 | Google Auth Platform 中 Web application 客户端 ID。 |
client_secret请求体 | string必填 | 本场景 Web application 客户端使用的服务端密钥,必填;其他客户端类型另有授权流程。 |
code请求体 | string必填 | 用户同意后回调返回的一次性授权码,由示例自动取得。 |
grant_type请求体 | string必填 | 固定 authorization_code。 |
redirect_uri请求体 | string必填 | 必须与后台注册和最初授权 URL 完全一致;本例 http://localhost:8765/oauth2callback。 |
调用代码
替换示例 ID 与请求体内容后运行。
# Python 3 标准库;保存为 google_oauth.py,在自己电脑运行。
import json, os, secrets, time, webbrowser
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlencode, urlparse, parse_qs
from urllib.request import Request, urlopen
from urllib.error import HTTPError
client_id = os.environ["GOOGLE_CLIENT_ID"]
client_secret = os.environ["GOOGLE_CLIENT_SECRET"]
scopes = os.environ.get("GOOGLE_SCOPES", "https://www.googleapis.com/auth/drive.metadata.readonly")
redirect_uri = "http://localhost:8765/oauth2callback"
state = secrets.token_urlsafe(32)
result = {}
class Callback(BaseHTTPRequestHandler):
def log_message(self, *args):
pass # 不把回调里的授权码写入访问日志
def do_GET(self):
parsed = urlparse(self.path)
if parsed.path != "/oauth2callback":
self.send_response(404); self.end_headers(); return
query = parse_qs(parsed.query)
if query.get("state", [""])[0] != state:
self.send_response(400); self.end_headers(); self.wfile.write(b"Invalid state"); return
if "error" in query:
result["error"] = query["error"][0]
elif "code" in query:
result["code"] = query["code"][0]
else:
result["error"] = "missing_authorization_code"
self.send_response(200); self.end_headers()
self.wfile.write(b"Authorization received. Return to your terminal.")
with HTTPServer(("127.0.0.1", 8765), Callback) as server:
server.timeout = 1
url = "https://accounts.google.com/o/oauth2/v2/auth?" + urlencode({
"client_id": client_id, "redirect_uri": redirect_uri,
"response_type": "code", "scope": scopes, "access_type": "offline",
"include_granted_scopes": "true", "prompt": "consent", "state": state
})
print("在浏览器确认自己申请的权限:\n" + url)
webbrowser.open(url)
deadline = time.monotonic() + 300
while not result and time.monotonic() < deadline:
server.handle_request()
if not result:
raise SystemExit("等待授权超时,请重新运行。")
if "error" in result:
raise SystemExit("授权失败:" + result["error"])
body = urlencode({"client_id": client_id, "client_secret": client_secret,
"code": result["code"], "grant_type": "authorization_code",
"redirect_uri": redirect_uri}).encode()
request = Request("https://oauth2.googleapis.com/token", data=body,
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
try:
with urlopen(request, timeout=30) as response:
tokens = json.load(response)
except HTTPError as exc:
raise SystemExit(f"令牌交换失败 HTTP {exc.code}: {exc.read().decode()}")
if not tokens.get("access_token"):
raise SystemExit("响应缺少 access_token")
# 令牌只保存在本机;不要上传此文件或提交 Git。
filename = "google-oauth-token.json"
fd = os.open(filename, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
os.fchmod(fd, 0o600)
with os.fdopen(fd, "w") as output:
json.dump(tokens, output, ensure_ascii=False, indent=2)
print("令牌已保存到本机 google-oauth-token.json;将 access_token 放入 GOOGLE_ACCESS_TOKEN。")
print("授权范围:", tokens.get("scope"), "有效秒数:", tokens.get("expires_in"))
返回示例
按官方字段整理的响应结构示意,ID、文本和数字均为虚构,非接口实测结果。expires_in 是有效秒数;refresh_token 不保证每次都返回,必须安全保留首次获取的值。 以下为结构示意,非本站实测结果。
{
"access_token": "example_access_token_not_real",
"expires_in": 3600,
"refresh_token": "example_refresh_token_not_real",
"scope": "https://www.googleapis.com/auth/drive.metadata.readonly",
"token_type": "Bearer"
}常见错误与排查
redirect_uri_mismatch回调地址与客户端注册不一致。- 核对协议、localhost、端口和 /oauth2callback 路径,完整登记到 Authorized redirect URIs。
invalid_grant授权码过期、已使用或刷新凭证无效。- 重新授权获取新的授权码;刷新令牌被撤销或失效时需要再次由用户同意。
access_denied用户或组织拒绝授权。- 确认用户确实同意、测试用户已添加且组织允许所需 scope;不要自动跳过授权。
调用注意
- 这是本机开发示例;仅监听 127.0.0.1:8765。公开服务需要自己实现 HTTPS 回调、用户会话和安全令牌存储。
- 示例使用普通 Bearer 令牌;官方另支持可选 DPoP 证明,本页不实现该扩展。
- 应用应检查返回 scope,用户可以只同意部分权限;不要以申请了 scope 代替确认已授予。
用刷新令牌获取新的访问令牌
此操作不要求用户每次重新登录,前提是已有仍有效的离线 refresh_token。
https://oauth2.googleapis.com/token请求头
Content-Typeapplication/x-www-form-urlencoded
参数说明
参数表可左右滑动,查看完整说明。
| 字段 / 位置 | 类型 / 必填 | 说明 |
|---|---|---|
client_id / client_secret请求体 | string必填 | 仍使用原授权客户端的凭证。 |
refresh_token请求体 | string必填 | 初次离线授权后安全保存的刷新令牌。 |
grant_type请求体 | string必填 | 固定 refresh_token。 |
调用代码
替换示例 ID 与请求体内容后运行。
# Python 3 标准库;先在自己终端设置下列环境变量。
import json, os
from urllib.parse import urlencode
from urllib.request import Request, urlopen
from urllib.error import HTTPError
body = urlencode({"client_id": os.environ["GOOGLE_CLIENT_ID"],
"client_secret": os.environ["GOOGLE_CLIENT_SECRET"],
"refresh_token": os.environ["GOOGLE_REFRESH_TOKEN"],
"grant_type": "refresh_token"}).encode()
request = Request("https://oauth2.googleapis.com/token", data=body,
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
try:
with urlopen(request, timeout=30) as response:
data = json.load(response)
except HTTPError as exc:
raise SystemExit(f"刷新失败 HTTP {exc.code}: {exc.read().decode()}")
if not data.get("access_token"):
raise SystemExit("响应缺少 access_token")
# 仅在自己的私密终端显示;生产系统应保存到安全的令牌存储。
print(json.dumps(data, ensure_ascii=False, indent=2))
返回示例
按官方字段整理的响应结构示意,ID、文本和数字均为虚构,非接口实测结果。把新的 access_token 用于后续 API;刷新响应通常没有新的 refresh_token,保留旧值。 以下为结构示意,非本站实测结果。
{
"access_token": "example_new_access_token_not_real",
"expires_in": 3600,
"token_type": "Bearer",
"scope": "https://www.googleapis.com/auth/drive.metadata.readonly"
}常见错误与排查
invalid_grant刷新令牌被撤销、过期或不再适用。- 停止重试并重新走用户同意流程;不能从客户端密钥推算出新令牌。
调用注意
- 测试状态、用户撤销、组织策略等都可影响刷新令牌寿命;程序需保存有效期并支持重新授权。
注意事项
- API Key 只能标识项目,不能访问用户私有文档;client_id 也不是可直接调用文件 API 的令牌。
- drive.file 可用于应用创建或用户通过 Picker 明确选择的文件,范围更小;它不代表读取用户 Drive 全盘。
- 本机令牌文件含敏感凭证,不要提交 Git;代码只供开发者本地执行,本站不会接收该文件。
核验来源
本站按官方资料整理本场景的必要调用步骤与代码,覆盖范围以本页列出的接口为准。官方资料用于追溯规范及查看变更。
- 01Google Web Server OAuth 2.0 授权码与刷新流程核验日期 2026-10-15
- 02Google OAuth 同意屏幕与测试用户核验日期 2026-10-15
- 03Drive scopes 与逐文件权限核验日期 2026-10-15
