Mini-HBUT 开发者平台生产环境
Quick Start
1. Discovery
标准 OpenID Connect Discovery 端点(机器可读值,必须使用 canonical 形式):
GET https://id.xn--vhq74jc2fzpchter27a.com/.well-known/openid-configuration
{
"issuer": "https://id.xn--vhq74jc2fzpchter27a.com",
"authorization_endpoint": "https://id.xn--vhq74jc2fzpchter27a.com/oauth/authorize",
"token_endpoint": "https://id.xn--vhq74jc2fzpchter27a.com/oauth/token",
"jwks_uri": "https://id.xn--vhq74jc2fzpchter27a.com/oauth/jwks",
"userinfo_endpoint": "https://id.xn--vhq74jc2fzpchter27a.com/oauth/userinfo"
}协议字段(iss、aud、JWKS、token 校验)只使用 https://id.xn--vhq74jc2fzpchter27a.com; 中文域名(id.湖北工业大学.com)仅用于人类展示,禁止在协议校验中使用。
2. Web 应用(Authorization Code + PKCE,服务端持有 secret)
// 服务端(Node + openid-client v6),绝不能在前端做授权码交换
import {
discovery, buildAuthorizationUrl, authorizationCodeGrant,
randomPKCECodeVerifier, calculatePKCECodeChallenge, randomState, randomNonce,
} from 'openid-client'
const config = await discovery(
new URL(process.env.ISSUER_URL), // canonical ASCII issuer
process.env.CLIENT_ID, // 你的 client_id
{ client_secret: process.env.CLIENT_SECRET, // secret 只存在服务端
redirect_uris: ['https://your-app.com/oauth/callback'],
token_endpoint_auth_method: 'client_secret_basic' },
)
// 1) 发起登录(PKCE S256;state/nonce/verifier 每次随机且与会话绑定)
const verifier = randomPKCECodeVerifier()
const state = randomState()
const nonce = randomNonce()
const authUrl = buildAuthorizationUrl(config, {
scope: 'openid profile', // 需要时再加敏感 scope
state,
nonce,
code_challenge: await calculatePKCECodeChallenge(verifier),
code_challenge_method: 'S256',
})
res.redirect(authUrl)
// 2) 回调:openid-client 自动校验 state/nonce/iss/aud/exp + PKCE 换码
const tokenSet = await authorizationCodeGrant(config, currentUrl, {
pkceCodeVerifier: verifier,
expectedState: state,
expectedNonce: nonce,
})
const claims = tokenSet.claims() // sub 为 pairwise 标识,与学号无关3. Native 应用(Public Client,PKCE S256)
// Native(无 secret,强制 PKCE S256;RFC 8252) // 1) 打开系统浏览器 openBrowser(authUrl) // https://<issuer>/oauth/authorize?client_id=...&redirect_uri=my-app:/oauth/callback&response_type=code&scope=openid&state=...&code_challenge=...&code_challenge_method=S256 // 2) 自定义 scheme 回调拿到 code 后,用 verifier 在服务端/自身交换 token // 禁止把 client_secret 内置进 App
Native 自定义 scheme 与 loopback(127.0.0.1 动态端口)遵循 RFC 8252; redirect_uri 必须与注册值逐字符精确匹配。
4. callback / state / nonce 注意事项
state:防 CSRF,必须随请求生成并在回调中严格校验;nonce:防重放,校验 id_token 中的 nonce 声明;code_verifier:只存在内存/会话,禁止写入 URL 或日志;- 授权码一次性:交换失败请重新发起登录,不要重试同一个 code。
5. 审核流程
创建应用(草稿)→ 提交审核 → 管理员批准(APPROVED)→ 正式启用(ACTIVE)→ 可在授权端点使用。被拒绝时按 Review 页反馈修改后重新提交。