第三方 OAuth 接入权限与安全
scope 与受众
权限清单怎么命名、哪些能申请、令牌里的 aud 到底决定了什么。
scope 回答「能做什么」,受众(aud)回答「能对谁做」。两者是两道独立的门:scope 过了、受众不对,请求照样被拒。
scope 的命名规范
自管理 scope 一律三段式:
<资源受众>:<对象>:<动作>例如 auth:sessions:read 表示「对 auth 资源服务器上的 sessions 对象执行 read」。好处是放行判定可以直接从名字推导,不需要第二张映射表;日志里一眼也能看出该向哪个资源申请。
OIDC 标准 scope(openid、profile、email、offline_access)没有前缀,它们不属于任何资源受众,单独归类。
你可以申请的 scope
可委托 scope
| scope | 语义 | 授权页展示 |
|---|---|---|
openid | 请求 OIDC 身份:换令牌时签发 id_token,并可用 UserInfo | 「验证你的身份」 |
profile | 读取昵称与显示名(preferred_username、name) | 「读取你的基本资料」 |
email | 读取邮箱与是否已验证(email、email_verified) | 「读取你的邮箱地址」 |
offline_access | 允许在用户离开后继续访问:签发刷新令牌 | 「在你不在时继续访问」 |
自管理 scope(第三方不可用)
| scope | 所属受众 | 语义 |
|---|---|---|
auth:credentials:read | auth | 列出用户的验证方式 |
auth:credentials:write | auth | 增删用户的验证方式 |
auth:sessions:read | auth | 列出活跃会话 |
auth:sessions:revoke | auth | 撤销会话 |
oauth:grants:read | oauth | 列出已授权的应用 |
oauth:grants:revoke | oauth | 撤销已授予的授权 |
account:profile:read | account | 读取账号扩展资料 |
account:profile:write | account | 更新账号扩展资料 |
这些 scope 表达的是「用户对自己的管理权」,只会出现在 MuoCeon 自己的管理界面,永远不会下放给第三方应用。
受众(aud)
| 受众 | 资源标识符 | 承载的接口 |
|---|---|---|
auth(默认) | {origin}/v1/auth | 登录态、验证方式、会话、UserInfo |
oauth | {origin}/v1/oauth | 已授权应用与授权关系管理 |
account | {origin}/v1/account | 账号扩展资料 |
第三方授权拿到的令牌,受众是 auth——因为你能做的事只有「读取用户身份」。想拿 oauth 或 account 受众的令牌,既没有对应 scope 可申请,也没有对应授权类型可用。
令牌里的 aud 同时包含代号与完整资源标识符:
{
"aud": ["auth", "https://id.example.com/v1/auth"]
}- 代号供服务内部路由比对;
- 完整 URI 供标准客户端与 RFC 9728 元数据里的
resource比对。
如果你的库按字符串数组严格匹配,请用完整 URI 那一项与受保护资源元数据比对,不要只认代号。
裁剪规则:申请了不等于拿到
服务端在授权码换令牌和刷新令牌两处都会按客户端能力裁剪 scope:
- 可委托 scope:放行。
- 自管理 scope:只有登记时被授予了对应受众的客户端才放行,第三方会被裁掉。
- 裁剪是静默丢弃,不报错——响应里的
scope就是最终生效的清单。
一定要读响应里的 scope
请求 openid profile email auth:sessions:read,实际拿到的 scope 可能只有
openid profile email。如果你按「我申请了就该有」写后续逻辑,就会在调用管理接口时
收到 403 insufficient_scope,而不是在换令牌时收到明确的错误。
另外两条边界:
- 刷新令牌时
scope只能收窄,出现原授权之外的值会被判invalid_scope。 client_credentials(machine客户端)不能请求offline_access,没有用户就没有可刷新的会话。
实践建议
- 按最小权限申请:只读身份就只要
openid profile email,不要顺手把offline_access也加上。 offline_access意味着你会在用户离线时仍能访问,同意页上会单独列出,滥用会显著降低授权率。- 不要把
sub之外的资料当作稳定标识:邮箱会变,昵称会变,sub不变。 - 每次刷新后都用响应里的
scope更新本地记录,别沿用旧值。