跳到主要内容

授权端点

GET /oauth/authorize​

引导用户进行 OAuth 授权。

请求参数​

参数类型必需说明
response_typestring是固定值 code
client_idstring是应用的 Client ID
redirect_uristring是回调地址,必须与注册时一致
scopestring否权限范围。默认 openid,可选 openid profile email
statestring推荐随机字符串,防 CSRF
promptstring否传 none 表示「不要交互」:任何情况下都不渲染页面,只回跳 code 或 error(OIDC 标准参数,接入站一般用不到)
code_challengestring视应用配置PKCE 挑战值,43-128 位 unreserved 字符(A-Z a-z 0-9 - . _ ~)
code_challenge_methodstring视应用配置固定 S256,本服务不支持 plain

验证失败返回​

条件响应
response_type 不是 code400 + 错误页
client_id 无效或已禁用400 + 错误页
redirect_uri 不在注册列表中400 + 错误页
scope 全部无效自动降级为 openid
code_challenge 格式非法(长度或字符集不对)400 + 错误页
code_challenge_method 不是 S256400 + 错误页
应用开启了「强制 PKCE」但没带 code_challenge400 + 错误页

行为​

条件行为
用户未登录存储请求到 Session → 跳转 /login → 登录后返回授权页
用户已授权且 scope 足够直接发放授权码 → 302 重定向到 redirect_uri?code=...
用户未授权或 scope 不足显示授权确认页
prompt=none(OIDC 标准)

带上 prompt=none 时,上表中「跳转 /login」和「显示授权确认页」两种情况都不渲染页面, 而是回跳 error=login_required / error=interaction_required。接入站已不做自动登录探测(见自动授权(免确认发码)),这个参数按规范保留。

已授权免确认机制​

系统会检查 user_authorizations 表中是否已有该用户对该应用的授权记录。如果当前请求的 scope 是已授权 scope 的子集,则自动发放授权码,不再弹出授权页面。

已授权 scope: "openid"
当前请求 scope: "openid"
→ 免确认,直接发放 code

PKCE(授权码拦截防护)​

授权请求带上 code_challenge,换取 Token 时再带上对应的 code_verifier。服务端只认「挑战值与验证值匹配」的那一次兑换:授权码即使在路上被截获,攻击者没有 verifier 也换不到 Token。

S256 的生成方式(code_challenge = code_verifier 的 SHA-256 摘要做 base64url,去掉末尾的 =):

$verifier = rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '='); // 43 字符
$challenge = rtrim(strtr(base64_encode(hash('sha256', $verifier, true)), '+/', '-_'), '=');

code_verifier 只保存在你自己的服务端,不要写进授权链接、前端代码或日志。

应用设置里有两项相关配置:

设置说明
客户端类型机密客户端用 Client Secret 认证;公共客户端不校验密钥,安全性由 PKCE 承担(桌面 App / SPA / CLI 这类拿不住密钥的应用选它)
强制该应用使用 PKCE开启后授权请求必须带 code_challenge,否则 400
只支持 S256,且默认策略对老应用友好

code_challenge_method 只接受 S256。plain 相当于把验证值明文放进 URL,起不到保护作用,本服务直接拒绝。

新建应用默认勾选「强制 PKCE」;已有应用默认不勾选,所以尚未实现 PKCE 的老接入方不受影响。公共客户端无论开关如何都强制 PKCE。

错误页​

验证失败会显示 HTML 错误页面(非 JSON),包含错误描述。


POST /oauth/authorize​

处理用户的授权确认/拒绝。

请求参数​

参数类型说明
_csrfstringCSRF Token(必须)
actionstringallow 确认授权 / deny 拒绝授权
client_idstringClient ID
redirect_uristring回调地址
scopestring请求的权限范围
statestring状态参数(透传)

响应​

授权成功(action=allow):

系统执行以下操作后重定向:

  1. 生成授权码(bin2hex(random_bytes(32)),64 字符 hex),并绑定本次授权请求的 code_challenge
  2. 存入 oauth_authorization_codes 表,10 分钟有效
  3. 更新 user_authorizations 记录(如已存在则更新 scope)
  4. 确保 user_app_subjects 中有该用户在该应用下的 subject_id
  5. 302 重定向:redirect_uri?code=AUTH_CODE&state=STATE

拒绝授权(action=deny):

302 重定向:redirect_uri?error=access_denied&state=STATE


GET /oauth/deny​

拒绝授权端点,用于 popup 模式下关闭窗口。

请求参数​

参数说明
state状态参数(透传)
redirect_uri回调地址(可选)

行为​

  • 有 redirect_uri:重定向到 redirect_uri?error=access_denied&state=...
  • 无 redirect_uri:执行 window.close() 关闭窗口