理解 OIDC 与 OAuth2.0 协议
OIDC 与 OAuth2.0 综述
在选择一种认证授权模式前,建议先理解 OAuth 2.0 和 OpenID Connect 协议,有助于选择最适合你的应用的授权模式。
基本认证 vs OAuth 2.0 vs OpenID Connect
目前 GenAuth 有三种可以选择的认证方式:
基本认证是基于 API 接口,通过发送账密、手机验证码到 GenAuth 后端的方式直接完成用户认证。提供 MFA、忘记密码等功能。GenAuth 的 Guard 组件以及 SDK 都基于这些 API。
OAuth 2.0 协议主要用于资源授权。
OpenID Connect 协议,简称 OIDC,是 OAuth 2.0 协议的超集,能够认证用户并完成资源授权。在可以选择 OIDC 的情况下,应该选择 OIDC。
如果你希望通过 API 的方式直接认证你的用户,可以查看当前项目中的 Node.js/JavaScript SDK 和 Python SDK 文档。
如果你希望实现单点登录或先鉴权用户再返回资源,建议使用 OIDC 协议。
OAuth 2.0
OAuth 2.0 是一个授权标准协议。如果你希望将自己应用的数据安全地授权给调用方,建议使用 OAuth 2.0。
根据 OAuth 2.0 协议规范,主要有四个主体:
- 授权服务器,负责颁发 Access Token,GenAuth 是授权服务器。
- 资源所有者,你的应用的用户是资源的所有者,授权其他人访问他的资源。
- 调用方,调用方请求获取 Access Token,经过用户授权后,GenAuth 为其颁发 Access Token。调用方可以携带 Access Token 到资源服务器访问用户的资源。
- 资源服务器,接受 Access Token,然后验证它的被赋予的权限项目,最后返回资源。
其他重要概念:
- 一次 OAuth 2.0 授权是指用户授权调用方相关的权限。
- Code 授权码是由授权服务器 GenAuth 颁发的,用于调用方使用 Code 换取 Token。
- Access Token 由授权服务器 GenAuth 颁发,持有 Access Token 说明完成了用户授权。
- Refresh Token 是一个可选的 Token,用于在 Access Token 过期后获取一个新的 Access Token。
常见的 OAuth 2.0 授权流程如下:
- 在你的应用中,让用户访问登录链接,浏览器跳转到 GenAuth,用户在 GenAuth 完成认证。
- 浏览器接收到一个从 GenAuth 服务器发来的授权码。
- 浏览器通过重定向将授权码发送到你的应用后端。
- 你的应用服务将授权码发送到 GenAuth 获取 AccessToken,如果需要,还会返回 refresh token。
- 你的应用后端现在知道了用户的身份,后续可以保存用户信息,重定向到前端其他页面,使用 AccessToken 调用资源方的其他 API 等等。
INFO
如果你想了解更多的 OAuth 2.0 内容,可以阅读协议规范。
OAuth 2.0 以及 OIDC 的核心就是授权服务器。授权服务器用于签发 Access Token。每个授权服务器都有一个唯一的 Issuer URI 和*签名密钥**。GenAuth 中每个应用都是一个授权服务器。
OpenID Connect
OpenID Connect 是基于 OAuth 2.0 的身份认证协议,增加了 Id Token。OIDC 也制定了 OAuth 2.0 中未定义部分的规范,例如 scope,服务发现,用户信息字段等。GenAuth 支持 OIDC。
在 OIDC 规范中,有些名词与 OAuth 2.0 有区别:
- OpenID Provider,指授权服务器,负责签发 Id Token。GenAuth 是 OpenID Provider。
- 终端用户,Id Token 的信息中会包含终端用户的信息。
- 调用方,请求 Id Token 的应用。
- Id Token 由 OpenID Provider 颁发,包含关于终端用户的信息字段。
- Claim 指终端用户信息字段。
OIDC 的授权流程与 OAuth 2.0 一样,主要区别在于 OIDC 授权流程中会额外返回 Id Token。
选择 OIDC 授权模式
你需要根据你的场景和你开发的应用类型选择一种合适的认证授权模式。本文将协助你选择合适的 OIDC 授权模式。
推荐的授权模式
不同类型的应用,需要选择不同的授权模式。下面的表格中是我们推荐的模式:
| 应用类型 | 授权模式 |
|---|---|
| 有后端场景 | 授权码模式 |
| SPA,无后端 | 隐式模式 |
| 服务器之间 | Client Credentials |
你的应用是否需要 Id Token?
| 授权模式 | Access Token | Id Token |
|---|---|---|
| 授权码模式 | ✅ | ✅ |
| 隐式模式 | ✅ | ✅ |
| 密码模式 | ✅ | ✅ |
| Client Credentials 模式 | ✅ | ❌ |
你的应用是什么类型?
如何选择 OIDC 授权模式取决于你在开发哪种类型的应用。参考以下流程图来选择你需要的授权模式:

你的应用代码是否能被公开访问
如果你的终端用户能够看到并修改你的应用代码,那么这个应用就是公开访问的。包括 SPA(单页 Web 应用)和移动端应用。这种场景下,应用无法安全地存储密钥。
你的应用是 SPA 还是原生应用?
如果你的应用是一个单页 Web 应用,运行在新版本的浏览器中,并且浏览器支持 Web Crypto,你应该使用 PKCE + 授权码模式。如果你的应用运行在老旧版本的浏览器中,浏览器不支持 Web Crypto,你应该使用隐式模式。隐式模式仅适用于应用无法安全存储密钥的场景,如果其他模式不可用时你才应该考虑用隐式模式。
如果你的应用是原生应用,你应该使用 PKCE + 授权码模式。
有没有终端用户在使用你的应用?
如果你的应用运行在服务器端,没有直接给终端用户使用,只是在进行服务器之间的交互,你应该使用 Client Credentials 模式。
应用和资源是否都被同一方持有?
如果你的应用以及应用需要访问的资源都是由你掌握,而且你的应用可以安全地存储用户账密,代码逻辑足够安全。当其他授权模式都不合适时,你可以选择密码模式。
授权码模式
授权码模式适合应用具备后端服务器的场景。授权码模式要求应用必须能够安全存储密钥,用于后续使用授权码换 Access Token。授权码模式需要通过浏览器与终端用户交互完成认证授权,然后通过浏览器重定向将授权码发送到后端服务,之后进行授权码换 Token 以及 Token 换用户信息。

了解更多信息,请参考成为联邦认证身份源。
隐式模式
隐式模式适合不能安全存储密钥的场景(例如前端浏览器)。在隐式模式中,应用不需要使用 code 换 token,无需请求 /token 端点,AccessToken 和 IdToken 会直接从认证端点返回。
INFO
因为隐式模式用于不能安全存储密钥的场景,所以隐式模式不支持获取 Refresh Token。

了解更多信息,请参考成为联邦认证身份源。
密码模式
密码模式适用于你既掌握应用程序又掌握应用所需资源的场景。密码模式要求应用能够安全存储密钥,并且能够被信任地存储资源所有者的账密。一般常见于自家应用使用自家的资源。密码模式不需要重定向跳转,只需要携带用户账密访问 Token 端点。

了解更多信息,请参考成为联邦认证身份源。
Client Credentials 模式
Client Credentials 模式用于进行服务器对服务器间的授权(M2M 授权),期间没有用户的参与。你需要创建编程访问账号,并将 AK、SK 密钥对交给你的资源调用方。
INFO
Client Credentials 模式不支持 Refresh Token。

了解更多信息,请参考成为联邦认证身份源。
OIDC 常见问题
OIDC 的全称是 OpenID Connect,是一个基于 OAuth 2.0 的轻量级认证 + 授权协议,是 OAuth 2.0 的超集。它规定了其他应用,例如你开发的应用 A(XX 邮件系统),应用 B(XX 聊天系统),应用 C(XX 文档系统),如何到你的中央数据表中取出用户数据,约定了交互方式、安全规范等,确保了你的用户能够在访问所有应用时,只需登录一遍,而不是反反复复地输入密码,而且遵循这些规范,你的用户认证环节会很安全。
OIDC 三种认证流程的特征对比
| 特性 | 授权码模式 | 隐式模式 | 混合模式 |
|---|---|---|---|
| 所有 token 全部从授权端点返回 | no | yes | no |
| 所有 token 都从 token 端点返回 | yes | no | no |
| token 不会暴露给前端 | yes | no | no |
| 客户端可以被 OP 认证 | yes | no | yes |
| 可以刷新 token | yes | no | yes |
| 一次交互 | no | yes | no |
| 必须服务器-服务器通信 | yes | no | varies |
不同 response-type 对应的授权流程
| "response_type" value | Flow |
|---|---|
| code | Authorization Code Flow(授权码模式) |
| id_token | Implicit Flow(隐式模式) |
| id_token token | Implicit Flow(隐式模式) |
| code id_token | Hybrid Flow(混合模式) |
| code token | Hybrid Flow(混合模式) |
| code id_token token | Hybrid Flow(混合模式) |
如何验证 Token 合法性
请参考:
/guides/basics/authenticate-first-user/how-to-validate-user-token.md
scope 参数对应的用户信息
| scope 名称 | 对应信息 |
|---|---|
| username | username |
| address | address |
| email,email_verified | |
| phone | phone_number, phone_number_verified |
| profile | birthdate,family_name,gender,given_name,locale,middle_name,name,nickname,picture,preferred_username,profile,updated_at,website,zoneinfo |
| offline_access | 如果存在此参数,token 接口会返回 refresh_token 字段 |
| roles | 对应 role 信息,用户的角色列表 |
| unionid | 用户的 unionid 字段 |
| openid | 用户的 openid 字段 |
| external_id | 用户在原有系统的用户 ID |
| extended_fields | 用户的扩展字段信息,内容为一个对象,key 为扩展字段名,value 为扩展字段值 |
以上为 GenAuth 默认支持的 Scope,你也可以在应用的「协议配置」功能区中进行「自定义 OIDC Scope」的配置。
OIDC 用户信息字段含义
| 字段名 | 翻译 |
|---|---|
| sub | subject 的缩写,唯一标识,一般为用户 ID |
| name | 姓名 |
| given_name | 名字 |
| family_name | 姓氏 |
| middle_name | 中间名 |
| nickname | 昵称 |
| preferred_username | 希望被称呼的名字 |
| profile | 基础资料 |
| picture | 头像 |
| website | 网站链接 |
| 电子邮箱 | |
| email_verified | 邮箱是否被认证 |
| gender | 性别 |
| birthdate | 生日 |
| zoneinfo | 时区 |
| locale | 区域 |
| phone_number | 手机号 |
| phone_number_verified | 认证手机号 |
| address | 地址 |
| formatted | 详细地址 |
| street_address | 街道地址 |
| locality | 城市 |
| region | 省 |
| postal_code | 邮编 |
| country | 国家 |
| updated_at | 信息更新时间 |
IdToken 与 AccessToken 的区别
IdToken 相当于用户的身份证,开发者的前端访问后端接口时应当携带 IdToken,开发者服务器应该校验用户的 IdToken,验证通过后返回相关资源,可用 OIDC 应用的密钥或 OIDC 应用公钥验签,然后可以得到此 token 对应的用户 ID 以及基本信息。示例代码请见:使用应用密钥验证 Token。
AccessToken 用于请求 GenAuth 服务器上该用户持有的资源。你访问 GenAuth 服务器的请求需要在 Authorization 请求头中携带此 AccessToken,示例代码如下:
const axios = require("axios");
axios
.get({
url: "https://core.authing.cn/api/v2/your/resources",
headers: {
Authorization: "Bearer YOUR_OIDC_ACCESS_TOKEN",
},
})
.then((res) => {
// custom codes
});为什么 OIDC 授权码流程要 code 换 token 再换用户信息
OIDC 授权码模式的认证流程中涉及三方:用户、OIDC 服务器(OIDC Provider,简称 OP)、应用业务服务器(Service Provider,简称 SP)。
SP、用户、OP 的交互目的分为以下几点:
- SP 希望拿到一个可信的身份断言,从而让用户登录。
- SP 发起登录,会跳转到 OP 的认证页面,OP 让用户登录,并授权自己的信息,然后 OP 将一个授权码(code)发给 SP。实际上这是在通过引用来传递用户信息。
- SP 收到授权码 code 后,结合 Client ID 和 Client Secret 到 OP 换取该用户的
access_token。 - SP 利用
access_token到 OP 去获取用户的相关信息,从而得到一个可信的身份断言,让用户登录。
OIDC 协议中,用户登录成功后,OIDC 认证服务器会将用户的浏览器回调到一个回调地址,并携带一个授权码(code)。此授权码一般有效期十分钟且一次有效,用后作废。这避免了在前端暴露 access_token 或者用户信息的风险,access_token 的有效期都比较长,一般为 1~2 个小时。如果泄露会对用户造成一定影响。
后端收到这个 code 之后,需要使用 Client Id + Client Secret + Code 去 OIDC 认证服务器换取用户的 access_token。在这一步,实际上 OIDC Server 对 OAuth Client 进行了认证,能够确保来 OIDC 认证服务器获取 access_token 的机器是可信任的,而不是任何一个人拿到 code 之后都能来 OIDC 认证服务器进行 code 换 token。
如果 code 被黑客获取到,如果他没有 Client Id + Client Secret 也无法使用,就算有,也要和真正的应用服务器竞争,因为 code 一次有效,用后作废,加大了攻击难度。相反,如果不经过 code 直接返回 access_token 或用户信息,那么一旦泄露就会对用户造成影响。