什么是 OAuth?“使用 Google 登录”与委托访问详解
OAuth 是一个开放标准,允许用户在不交出密码的情况下,授予一个应用对其另一项服务账户的有限访问权限。当你点击“连接 Google 日历”,然后看到一个询问“允许此应用查看你的日程吗?”的界面时,那就是 OAuth。应用获得的令牌仅对你所批准的内容有效,并且你可以随时撤销。同样的机制,再加上一层名为 OpenID Connect 的扩展,就驱动了“使用 Google 登录”以及类似的按钮。
用 AI 构建时为什么 OAuth 很重要
构建应用时,OAuth 通常出现在两种情况中:
- 社交登录。 你希望用户使用“使用 Google 登录”(或 Apple、Microsoft、GitHub),而不是再创建一个新密码。参见如何为你的应用添加登录功能。
- 连接用户在其他服务上的账户。 你的应用需要读取用户的日历、代表其在社交账户上发布内容、从其云盘拉取文件,或同步其会计数据。
在这两种情况下,AI 构建器都可以编写代码,但有些步骤只能由你来完成:在服务商处创建开发者应用、获取客户端 ID 和客户端密钥、注册回调地址,以及对某些权限通过服务商的审核。了解 OAuth 的工作原理,会让这些步骤及其产生的错误不再那么神秘。关于登录的更宏观内容,参见什么是身份认证。
一个日常类比
想象一下酒店的房卡。办理入住时,前台确认你的身份,然后给你一张房卡,它可以打开你的房间和健身房——而不能打开其他房间或员工区域——并且在退房后失效。房卡上并不包含你的护照信息。如果你丢了它,前台会将其注销并重新发一张。
- 前台是授权服务器(即 Google 的登录页面)。
- 你的身份核验是你在 Google 上登录。
- 房卡就是访问令牌。
- **“仅限房间和健身房”**就是你批准的权限范围(scope)。
- 退房则是令牌过期,或你撤销了访问权限。
你正在使用的应用从未看到你的 Google 密码,它只拿到了这张房卡。
OAuth 如何一步步工作
Web 应用最常用的流程是授权码流程(authorization code flow)。下面是用户把 Google 日历连接到一个日程安排应用时发生的过程:
- 点击。 用户点击“连接 Google 日历”。
- 重定向到服务商。 应用将浏览器发送到 Google,并附上其客户端 ID、希望获得的权限范围(读取日历事件),以及 Google 在完成后应把用户送回的重定向 URI。
- 登录并同意。 用户登录 Google(如尚未登录),并看到一个列出应用所请求内容的同意界面。他们点击“允许”。
- 带着代码返回。 Google 将浏览器重定向回应用的重定向 URI,并附上一个短期有效、仅能使用一次的授权码。
- 在服务器上交换。 应用的服务器将该代码连同其客户端密钥直接发送给 Google,并获得一个访问令牌(通常还有一个刷新令牌)。
- 使用 API。 服务器用访问令牌调用 Google 日历 API,获取用户的日程事件。
第 4 步中的回调大致如下:
https://yourapp.com/auth/google/callback?code=4/0Adeu5B...&state=x7Kq9
第 5 步中的令牌响应是这样的 JSON:
{
"access_token": "ya29.a0Af...",
"expires_in": 3599,
"refresh_token": "1//0gL...",
"scope": "https://www.googleapis.com/auth/calendar.readonly",
"token_type": "Bearer"
}
关键的设计要点是:令牌交换发生在服务器与服务器之间,因此客户端密钥和令牌无需经过浏览器。
OAuth、OpenID Connect 与你自己的登录
| 回答的问题 | 示例 | |
|---|---|---|
| OAuth 2.0 | “这个应用被允许代表用户做什么?”(授权) | 读取用户的日历 |
| OpenID Connect(OIDC) | “这个用户是谁?”(认证),构建于 OAuth 之上 | “使用 Google 登录”返回一个已验证的邮箱 |
| 你自己的登录 | “这是此账户的正确密码吗?” | 在你的数据库中存储的(哈希后的)邮箱和密码 |
许多应用会将它们组合使用:邮箱加密码登录,加上通过 OIDC 实现的“使用 Google 登录”,再加上对其他服务的独立 OAuth 连接。
关键术语
| 术语 | 含义 |
|---|---|
| 服务商 / 授权服务器(Provider / authorization server) | 用户登录并授予访问权限的服务——Google、Microsoft、GitHub。 |
| 客户端(Client) | 你的应用,已在服务商处注册。 |
| 客户端 ID(Client ID) | 你的应用在服务商处的公开标识符。 |
| 客户端密钥(Client secret) | 你的应用在服务商处的私有密码。仅限服务器端使用。 |
| 权限范围(Scope) | 一项具体的权限,例如“读取日历”或“查看邮箱地址”。 |
| 同意界面(Consent screen) | 用户查看并批准所请求权限范围的页面。 |
| 重定向 URI / 回调地址 | 服务商把用户送回的地址。必须事先注册,并且完全匹配。 |
| 访问令牌(Access token) | 用于调用 API 的短期凭证。 |
| 刷新令牌(Refresh token) | 用于获取新访问令牌的长期凭证。请像对待密码一样保存它。 |
| ID 令牌(ID token) | 在 OIDC 中,描述用户身份的签名令牌。 |
| PKCE | 一种额外的校验(“证明密钥”),用于防止被截获的代码被他人使用。建议所有客户端都使用。 |
| state | 一个随机值,用于防止伪造请求,保护整个流程。 |
常见误解
- “OAuth 是一种登录系统。” 纯粹的 OAuth 只负责授予访问权限;身份识别由 OpenID Connect 负责。把单纯的访问令牌当作某人身份的证明,曾经导致过真实的安全漏洞。
- “应用会拿到我的密码。” 它永远不会。这正是设计的初衷。
- “请求更多权限范围是无害的。” 用户对范围过宽的请求会更加警惕,而且 Google 等服务商可能要求对敏感或受限的权限范围进行应用审核。只请求你真正需要的。
- “客户端密钥可以放在前端。” 浏览器端的任何代码都是公开的。请把它保存在服务器端密钥中。
- “测试时能用,正式上线也就能用。” 服务商通常会限制未经验证的应用,例如只允许列出的测试用户使用;而你正式域名的重定向 URI 必须另行注册。
- “令牌永远有效。” 访问令牌很快就会过期,用户也可以撤销访问权限。你的应用必须妥善处理这两种情况。
向 AI 构建器提出的请求
- “使用 OpenID Connect 添加‘使用 Google 登录’,并借助维护良好的认证库——不要自己手写 OAuth 流程。”
- “使用带 PKCE 的授权码流程,并校验
state参数。” - “在服务器端从环境变量中读取客户端 ID 和客户端密钥。”
- “只请求我们需要的权限范围:只读的日历访问。”
- “请告诉我需要在服务商控制台中注册的所有重定向地址,包括预览地址、已发布的网址以及我的自定义域名。”
- “把刷新令牌加密后存入数据库,并通过请用户重新连接来处理过期或被撤销的令牌。”
- “让用户可以在设置页面中断开该集成。”
Mythex 中的 OAuth
Mythex 并不为你构建的应用提供内置的终端用户登录功能;其文档描述的是“自带方案”的方式:你选择一个认证库或托管的服务商,把它的密钥保存为项目密钥,然后由智能体编写代码。添加身份认证配方提供了一条起始提示词。你也会在 Mythex 本身遇到 OAuth:你可以使用 Google 注册,连接 GitHub 进行仓库同步会使用 GitHub OAuth,而通过 MCP 连接到 Mythex 的 AI 客户端则会经过一个 OAuth 同意页面。要了解更多,参见什么是身份认证和什么是双因素认证。
常见问题
用简单的话说,什么是 OAuth?
OAuth 是一种标准方式,让用户可以允许一个应用访问他们在另一项服务上的账户,例如允许日程应用读取他们的 Google 日历,而无需把密码交给该应用。用户批准特定的权限,应用则获得一个只能用于这些权限的令牌。
OAuth 和“使用 Google 登录”是一回事吗?
“使用 Google 登录”建立在 OAuth 2.0 加上 OpenID Connect 之上。OpenID Connect 是 OAuth 之上的一层,增加了一种标准方式来告诉应用用户是谁。OAuth 本身关注的是授予访问权限;而 OpenID Connect 则让它变成了一种登录方式。
什么是 OAuth 访问令牌?
访问令牌是应用发送给服务 API 的凭证,用于证明它已获得访问授权。它通常有效期较短,并且仅限于用户批准的权限(scope)。如果颁发了刷新令牌(refresh token),应用就可以在无需再次询问用户的情况下获取新的访问令牌。
redirect_uri_mismatch 是什么意思?
它表示你的应用在登录时发送的回调地址,与服务商开发者控制台中注册的地址不完全一致。请为你的应用所运行的每一个地址,添加完全相同的 URL——包括 https、域名、路径以及末尾的斜杠。
OAuth 安全吗?
只要实现正确,OAuth 2.0 是安全且被广泛使用的。正确的做法包括:使用带 PKCE 的授权码流程、精确的重定向地址、state 参数,并把客户端密钥和令牌保存在服务器端。大多数问题都源于实现上的捷径,因此建议使用维护良好的成熟库。