RankHub

前端接入(JS SDK)

一段 script 挂登录按钮、开小窗授权,或把授权面内嵌进自己的页面;含完整 API 与浏览器边界。

不想自己实现授权跳转?在页面上引一段脚本:

<script src="https://auth.rankhub.cc/oauth/sdk.js" defer></script>
<div id="login"></div>
<script>
  window.addEventListener('DOMContentLoaded', function () {
    RankHubAuth.button('#login', {
      clientId: 'your-client-id',
      redirectUri: 'https://app.example.com/auth/callback',
      scope: 'openid profile',
      variant: 'filled',
      onSuccess: function (result) { console.log(result.code); },
      onError: function (error) { console.warn(error.code, error.message); },
    });
  });
</script>

SDK 只做三件事:渲染按钮、开小窗 / 挂 iframe、收 postMessage。它自己从不调用 Auth 接口 ——所有协议调用都发生在 auth 域自己的页面里(同源),所以不需要你配 CORS,也不依赖第三方 Cookie。

小窗还是内嵌

小窗(button / authorize)内嵌(embed)
入口点击按钮 → 480×720 小窗iframe 挂在你的页面里(inline 或 overlay 弹层)
页面路径/oauth/authorize(禁止被框)/oauth/embed(按登记的来源算 frame-ancestors)
授权码怎么回来小窗获批后跳到 redirect_uri,那一页再引同一段脚本自动把结果 postMessage 回宿主iframe 不跳转,直接把 code postMessage 给宿主
你要准备什么一个与宿主页同源的回调页把宿主 origin 登记进 embed_origins(见下)

小窗流程唯一必须额外写的代码,是一个回调页:

<!-- https://app.example.com/auth/callback -->
<script src="https://auth.rankhub.cc/oauth/sdk.js"></script>

就这些 —— SDK 加载时会发现「我在小窗里 + URL 里有 code + 本地存着这次请求」,于是投递结果并关窗。

API

RankHubAuth.config(options)

选项默认说明
authOrigin脚本自身 origin通常不用改
clientId—必填
redirectUri当前页面必须逐字登记过
scope空(服务端默认 openid profile)空格分隔
theme'auto'内嵌模式生效
popupFeatures'popup=yes,width=480,height=720'不要加 noopener

RankHubAuth.button(target, options)

选项说明
variantfilled / tonal / outlined / text / brand-dark
sizesm / md / lg
label按钮文案(默认「通过 RankHub 继续」)
logotrue / false / 图片 URL
styles追加到 Shadow DOM 的 CSS
render(ctx) => HTMLElement,完全自定义
onSuccess(result) / onError(error)结果回调,result 是 { code, state }
fallbackToRedirect小窗被拦时是否自动退整页跳转(默认 true)

按钮渲染在 Shadow DOM 里,宿主页的 button { … } 不会影响它。要改样式用 CSS 变量:

#login { --rankhub-auth-bg: #0f766e; --rankhub-auth-fg: #fff; --rankhub-auth-radius: 999px; }

RankHubAuth.embed(target, options)

选项说明
modeinline(默认)/ overlay(居中弹层,Esc / 关闭按钮 / 点遮罩可关)
height像素或 'auto'
theme覆盖深浅色
onStatus(status)waiting / scanning
onDeepLink(url)授权页请求做顶层跳转去打开 App(默认就是 location.href = url)
onError(error)失败回调
const handle = RankHubAuth.embed('#slot', { mode: 'overlay', height: 560 });
handle.result.then(r => console.log(r.code)).catch(e => console.warn(e.code));

ready 在授权面确认「我被允许框住」之后 resolve;8 秒内没有任何消息会以 origin_not_registered 报错(通常是来源没登记,或浏览器按 frame-ancestors 拦掉了)。

其他方法

方法用途
authorize(options)小窗流程(button 内部用它),resolve { code, state }
redirect(options)整页跳转(小窗被拦时的兜底);不 resolve
handleRedirect(options)回调页手动消费;返回 {code,state} / {error,state} / {delivered:'opener'} / null
exchange({ code, state })公开客户端用授权码换令牌,返回令牌端点 JSON
auto(root)扫描 [data-rankhub-auth] 并挂载
config(options)设全局默认值

回调页可以只靠声明式属性挂载(脚本标签加 data-auto="on" 时自动扫描):

<div data-rankhub-auth="button" data-client-id="…" data-redirect-uri="…"
     data-variant="outlined" data-label="登录"></div>

exchange 只给公开客户端

RankHubAuth.exchange() 会在浏览器里调令牌端点,因此只能用于没有 client_secret 的公开客户端。 机密客户端请在服务端换令牌,把结果以你自己的会话形式下发给前端。SDK 也不替你保存令牌。

错误码

error 是 RankHubAuthError(error.code / error.message / error.state)。

码含义
not_configured缺 clientId / redirectUri
popup_blocked浏览器拦了小窗(默认自动退整页跳转)
popup_closed用户关掉了小窗/弹层
timeout10 分钟没有任何结果
access_denied用户在 App 里拒绝
login_required静默(prompt=none)时没有可用登录态
origin_not_registered内嵌来源未登记,或授权面没加载出来
invalid_client / redirect_uri_not_registered / scope_not_allowed / pkce_required / state_required / invalid_request请求不合法(内嵌模式下会在框里说明原因)
expired授权请求已过期(默认 5 分钟)
storage_unavailable浏览器不允许本地存储 ⇒ 小窗流程无法完成(内嵌不受影响)
network换令牌时网络失败

浏览器边界

  • 不依赖第三方 Cookie:所有协议调用都在 auth 域内完成,Safari 的跨站跟踪防护不影响内嵌模式。
  • 内嵌模式不支持 prompt=none:iframe 里的存储/会话本来就被拦,服务端会直接拒绝。
  • 内嵌模式可能不显示「近期账号」列表:那份列表存在 auth 域的 localStorage,第三方 iframe 里 可能被隔离。流程本身不受影响(走「使用其他账号」)。
  • 小窗的回调页必须与宿主页同源:state / code_verifier 存在宿主页自己的存储里。
  • 一次性:code 用一次就作废,state 每次请求新生成。
  • 脚本 URL 稳定(/oauth/sdk.js),内容可能修 bug;破坏性变更会换路径。

登记可内嵌来源

要把授权面内嵌进 https://app.example.com,把该裸 origin 填进 开发者平台 里该应用的 可嵌入来源(Embed Origins) 字段 (创建或编辑时都能改,保存后立即生效,不需要重新审核)。详见注册应用与凭据。

规则:只允许 https(http 仅限 localhost / 127.0.0.1 / [::1])、不带路径/查询/片段、 小写 host、最多 20 项。登记一个来源 = 允许把该客户端的授权码交付给它,所以只登记你控制的域名。

未登记的来源仍然能被浏览器加载页面,但拿不到授权结果,而且不会创建任何授权请求。

本页内容