前端接入(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)
| 选项 | 说明 |
|---|---|
variant | filled / tonal / outlined / text / brand-dark |
size | sm / md / lg |
label | 按钮文案(默认「通过 RankHub 继续」) |
logo | true / 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)
| 选项 | 说明 |
|---|---|
mode | inline(默认)/ 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 | 用户关掉了小窗/弹层 |
timeout | 10 分钟没有任何结果 |
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 项。登记一个来源 = 允许把该客户端的授权码交付给它,所以只登记你控制的域名。
未登记的来源仍然能被浏览器加载页面,但拿不到授权结果,而且不会创建任何授权请求。