JavaScript SDK
JavaScript SDK 集成指南,支持浏览器 OAuth 2.1 + PKCE 流程。
安装
UM JS SDK 是单文件 SDK,直接 <script> 引入即可:
<script src="https://um.yunjii.cn/sdk/um-sdk.js"></script>
初始化
<script>
UM.config.appid = 'your_appid';
UM.config.appkey = 'your_appkey';
UM.config.redirect_uri = 'https://your-app.com/callback.html';
</script>
浏览器端 appkey 会暴露在前端代码中。生产环境务必在 BFF 层(如 Next.js API Routes)调用需要签名的接口(token / refreshToken),前端只调无需签名的接口(authorizeUrl / qrcodeUrl)。
OAuth 2.1 + PKCE 流程(浏览器端)
1. 生成授权 URL + 跳转
async function login() {
const state = 'state_' + Date.now() + '_' + Math.random().toString(36).slice(2);
const verifier = UM.generateCodeVerifier();
const challenge = await UM.generateCodeChallenge(verifier);
sessionStorage.setItem('um_pkce_state', state);
sessionStorage.setItem('um_pkce_verifier', verifier);
window.location.href = UM.authorizeUrl(state, challenge);
}
2. 回调页处理(code 换 token)
推荐通过 BFF 后端换取 token(避免 appkey 暴露)。下方为前端直连示例,仅适合 demo / 内部工具。
// 回调页(URL 带 ?code=xxx&state=xxx 时自动执行)
async function handleCallback() {
const params = new URLSearchParams(location.search);
const code = params.get('code');
const state = params.get('state');
// 防 CSRF
if (state !== sessionStorage.getItem('um_pkce_state')) {
throw new Error('State mismatch');
}
// 用 code + code_verifier 换 token(⚠️ 建议走 BFF 后端)
const verifier = sessionStorage.getItem('um_pkce_verifier');
const tokenRes = await UM.token(code, verifier);
if (tokenRes.code !== 0) {
throw new Error('Token error: ' + tokenRes.msg);
}
// 存储 token
localStorage.setItem('um_access_token', tokenRes.access_token);
localStorage.setItem('um_refresh_token', tokenRes.refresh_token);
// 清理 PKCE 临时数据
sessionStorage.removeItem('um_pkce_state');
sessionStorage.removeItem('um_pkce_verifier');
// 跳转登录后页面
location.href = '/dashboard';
}
if (location.search.includes('code=')) {
handleCallback();
}
3. 获取用户信息
const accessToken = localStorage.getItem('um_access_token');
const user = await UM.userinfo(accessToken);
console.log(user);
// => { sub: 'um_xxx', name: '张三', nickname: '张三', picture: '...', login_type: 'wx', ... }
4. 刷新 Token
const refreshToken = localStorage.getItem('um_refresh_token');
const newToken = await UM.refreshToken(refreshToken);
// ⚠️ 旧 refresh_token 已失效,必须更新
localStorage.setItem('um_access_token', newToken.access_token);
localStorage.setItem('um_refresh_token', newToken.refresh_token);
扫码登录(二维码图片模式)
适合 PC 端,直接渲染二维码图片,无需跳转:
<div id="um-login"></div>
<script>
UM.init({
mode: 'qrcode',
appid: 'your_appid',
appkey: 'your_appkey',
element: '#um-login',
type: 'wx',
redirect_uri: 'https://your-app.com/callback.html',
onLogin: function(res) {
location.href = '/dashboard';
}
});
</script>
支持的 mode:qrcode(推荐)/ redirect / popup / embed(不推荐)。
Node.js / Next.js BFF(推荐)
生产环境应在后端处理 token 交换,避免 appkey 暴露:
// app/api/auth/callback/route.ts
import { NextResponse } from 'next/server';
import { cookies } from 'next/headers';
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const code = searchParams.get('code')!;
const verifier = cookies().get('um_pkce_verifier')?.value;
// 服务端调用 UM token 端点(appkey 安全)
const tokenRes = await fetch('https://um.yunjii.cn/oauth/token.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code,
client_id: process.env.UM_APPID!,
client_secret: process.env.UM_APPKEY!,
redirect_uri: process.env.UM_CALLBACK!,
code_verifier: verifier,
}),
}).then(r => r.json());
if (tokenRes.code !== 0) {
return NextResponse.redirect(new URL('/login?error=token', request.url));
}
// 存储 token(httpOnly cookie)
cookies().set('um_access_token', tokenRes.access_token, {
httpOnly: true, secure: true, sameSite: 'lax',
maxAge: 60 * 60 * 24 * 7,
});
cookies().set('um_refresh_token', tokenRes.refresh_token, {
httpOnly: true, secure: true, sameSite: 'lax',
maxAge: 60 * 60 * 24 * 30,
});
return NextResponse.redirect(new URL('/dashboard', request.url));
}
错误处理
try {
const token = await UM.token(code, verifier);
if (token.code !== 0) {
console.error('errcode:', token.errcode, 'msg:', token.msg);
}
} catch (err) {
console.error('Network error:', err);
}
相关文档
- OAuth 2.1 API 参考 — 端点完整说明
- PHP SDK — 后端 SDK 集成
- 对接指南 — 5 步完成接入