RankHub

OIDC 与用户身份

用 RankHub 登录:发现文档、JWKS、id_token 声明与校验、userinfo,以及和通用 OIDC 库对接时的坑。

RankHub 的 OIDC 是最小子集:够你做「用 RankHub 登录」,不提供 RP 发起的登出、会话管理 (check_session)等扩展。

发现文档

GET https://auth.rankhub.cc/v1/oidc/.well-known/openid-configuration

不是标准路径:通用库的自动发现会 404

标准推导是 {issuer}/.well-known/openid-configuration,而这里的 issuer 是 https://auth.rankhub.cc —— 按标准推导会请求 https://auth.rankhub.cc/.well-known/openid-configuration,返回 404。

必须显式指定发现地址:

  • 支持 discoveryUrl / metadataUrl 的库:填 https://auth.rankhub.cc/v1/oidc/.well-known/openid-configuration;
  • 只接受 issuer 并自己拼路径的库(不少 js/Go 客户端):改成手填端点 (authorization / token / userinfo / jwks 四条,见下表),然后把 issuer 单独固定成 https://auth.rankhub.cc;
  • 最省事的做法:只把 RankHub 当「授权码 + 校验 id_token」用,不依赖任何自动发现。
字段值
issuerhttps://auth.rankhub.cc
authorization_endpointhttps://auth.rankhub.cc/v1/oauth/authorize(协议端点,非页面)
token_endpointhttps://auth.rankhub.cc/v1/oauth/token
userinfo_endpointhttps://auth.rankhub.cc/v1/oidc/userinfo
jwks_urihttps://auth.rankhub.cc/v1/oidc/jwks
revocation_endpointhttps://auth.rankhub.cc/v1/oauth/revoke
introspection_endpointhttps://auth.rankhub.cc/v1/oauth/introspect
id_token_signing_alg_values_supported["ES256"]
code_challenge_methods_supported["S256"]
response_types_supported["code"]
grant_types_supportedauthorization_code、refresh_token、urn:ietf:params:oauth:grant-type:device_code

发现文档与 JWKS 都带 Cache-Control: public, max-age=3600,可以缓存一小时;签名密钥轮换时 JWKS 会同时出现新旧 kid,按 kid 选密钥,不要按顺序取第一把。

JWKS

curl -s https://auth.rankhub.cc/v1/oidc/jwks
{
  "keys": [
    {
      "alg": "ES256",
      "crv": "P-256",
      "kid": "rankhub-oidc-1",
      "kty": "EC",
      "use": "sig",
      "x": "…",
      "y": "…"
    }
  ]
}

只有公开参数。注意:JWKS 端点在没有可用签名器时会返回 { "keys": [] }(不是 500), 所以验签失败要先看 JWKS 是不是空的,不要一律当成客户端 bug。

id_token 声明

声明说明
iss恒为 https://auth.rankhub.cc
sub用户唯一标识(RankHub 账号 id),在你的应用内作为主键使用它
aud你的 client_id(字符串,不是数组)
iat / exp签发/过期;exp = iat + 3600(1 小时)
jti<sub>:<iat>,仅用于去重
name用户显示名;为空时服务端会填「RankHub 用户」
email / email_verified只有用户账号绑定了邮箱才出现 email 键
phone_number只有绑定了手机号才出现
nonce授权请求带了才回显
cnf.jkt恒存在:令牌绑定的 DPoP 公钥指纹;未绑定令牌的值是哨兵串 unbound(不是真实指纹)

没有 preferred_username,没有 at_hash / c_hash(所以不要配置库去校验它们)。

校验 id_token

import { createRemoteJWKSet, jwtVerify } from 'jose';

const jwks = createRemoteJWKSet(new URL('https://auth.rankhub.cc/v1/oidc/jwks'));

const { payload } = await jwtVerify(idToken, jwks, {
  issuer: 'https://auth.rankhub.cc',   // 必须逐字一致
  audience: 'your-client-id',          // 你的 client_id
  algorithms: ['ES256'],               // 别放开成 ["none"] 或 HS256
});

if (payload.nonce !== expectedNonce) throw new Error('nonce mismatch');

必查项:签名算法白名单(只允许 ES256)、iss、aud、exp(允许少量时钟偏差)、 nonce(带了就必须比对)。cnf.jkt 只在你要做 DPoP 绑定时才有意义,普通 Web 应用忽略它。

userinfo

curl -s https://auth.rankhub.cc/v1/oidc/userinfo \
  -H "Authorization: Bearer $ACCESS_TOKEN"
{
  "sub": "…",
  "name": "…",
  "preferred_username": "…",
  "email": "…",
  "email_verified": true,
  "phone_number": null,
  "phone_number_verified": false
}
  • preferred_username 目前取的就是显示名 name,可能与 name 相同或为空串。
  • email / phone_number 未绑定时是 null。
  • 失败也是 200

    令牌无效/过期时返回的仍是 HTTP 200,body 是 { "error": "invalid_token", "error_description": "The access token is invalid or expired" }。 很多 OIDC 库只在非 2xx 时才抛错,会把这种响应当成"成功的空用户"。 请显式检查 body 里有没有 error 字段。
  • userinfo 只认 Authorization: Bearer。带 DPoP 方案(Authorization: DPoP + proof)也支持, 但那条通道会额外要求令牌含 openid scope。

scope 与资料的对应

scope能拿到什么
openidsub(+ id_token)。只要登录就至少要有它
profilename、preferred_username
emailemail、email_verified(用户绑定了才有值)
phonephone_number、phone_number_verified
offline_access语义上表示"允许离线续期";实际授权码换令牌总会拿到 refresh token

userinfo 的 Bearer 通道不强制校验 scope,所以即使只申请了 openid 也能拿到公开资料字段。 但仍建议按需申请:用户在授权页看到的是 scope 文案,多申请会降低授权通过率。

登出

没有 end_session_endpoint,post_logout_redirect_uris 也还没有任何端点在使用。所以:

  1. 清掉你自己应用里的会话(cookie / 本地存储);
  2. 调 POST https://auth.rankhub.cc/v1/oauth/revoke,body token=<refresh_token>,把令牌族撤掉 —— 响应恒为 200 {}(幂等,不需要客户端认证);
  3. 不要指望「点一下退出,RankHub 那边也退登」—— 那需要用户在 RankHub App 内操作。

与通用库对接的检查清单

  • 发现地址显式配置成 /v1/oidc/.well-known/openid-configuration;
  • 库里 issuer 固定为 https://auth.rankhub.cc(不要用"发现文档里的 issuer 与其 URL 不一致"的错误兜底绕开);
  • aud 校验成字符串 client_id(有些库默认期望数组);
  • 不要求 at_hash;
  • userinfo 显式判 error 字段;
  • 不配置 token endpoint 的 private_key_jwt / client_secret_jwt(不支持)。

本页内容