Beats ← 返回门户

Beats ID 统一身份认证 · 接入技术规范

版本:v1.1 | 生效日期:2026-08-18 | 维护方:Beats 彼邑网络 AI 实验室


1. 概述

Beats ID 是 beats.yun 云平台的统一账号体系。任何部署在该平台上的应用(Web 系统、内部工具、SaaS 产品),都可以接入 Beats ID,让用户使用平台账号一键登录,无需二次注册

名词约定

名词 含义
Beats ID / 平台 beats.yun,账号与授权服务所在方
应用 / Client 你开发的、要接入平台登录的系统
资源所有者 最终用户

2. 接入前准备:注册应用客户端

每个应用在接入前,必须先在平台登记,获得一对凭证:

凭证 说明
client_id 应用标识,形如 beatsapp_xxxxxxxx,可公开
client_secret 应用密钥,仅创建时返回一次,只存于你应用的后端,绝不可出现在前端代码、App 包体或公开仓库中

登记方式:联系平台管理员,提供①应用名称 ②回调地址(redirect_uri,可多个)。管理员通过管理接口创建后把凭证交给你。

管理员自助接口(需管理员账号会话): POST /api/admin/oauth/clients,Body:{"name":"应用名","redirect_uris":["https://你的域名/callback"]}

回调地址规则:授权完成后平台只把用户送回登记过的地址,精确匹配(含协议、域名、端口、路径),不支持通配符。请至少登记一个生产地址;本地开发可额外登记 http://127.0.0.1:端口/callback


3. 登录流程(四步)

用户浏览器                你的应用                 beats.yun 平台
    │ ① 点"使用 Beats ID 登录" │                        │
    │────────────────────────>│                        │
    │   ② 302 跳转到平台授权页  │                        │
    │─────────────────────────────────────────────────>│
    │        用户在平台登录并点"同意授权"                  │
    │<─────────────────────────────────────────────────│
    │   ③ 带回授权码 code 跳回你的回调地址                 │
    │────────────────────────>│                        │
    │                         │ ④ 后端用 code 换 token   │
    │                         │───────────────────────>│
    │                         │<── access_token ───────│
    │                         │ 用 token 拉取用户信息    │
    │                         │───────────────────────>│
    │<── 登录成功,建立你的本地会话 ──│                        │

第 1 步:引导用户跳转到授权页

把「使用 Beats ID 登录」按钮链接到:

GET {平台地址}/oauth/authorize
    ?client_id=你的client_id
    &redirect_uri=你的回调地址(需 URL 编码)
    &state=随机防串改字符串
参数 必填 说明
client_id 登记时获得
redirect_uri 必须与登记的回调地址完全一致
state 强烈建议 你生成的随机串,第 3 步原样带回,用于防 CSRF 攻击。务必校验

第 2 步:平台将用户送回你的回调地址

用户同意后,浏览器 302 跳转:

{你的redirect_uri}?code=授权码&state=你第1步传的state

第 3 步:后端用授权码换访问令牌

必须在你的服务端发起(要用到 client_secret):

POST {平台地址}/oauth/token
Content-Type: application/json

{
  "grant_type": "authorization_code",
  "client_id": "你的client_id",
  "client_secret": "你的client_secret",
  "code": "第2步拿到的code",
  "redirect_uri": "与第1步完全一致的回调地址"
}

成功响应:

{
  "access_token": "R9Brv31Gsg5KoMzl12Td43S20yrVrPN2yjnTUDMrwvE",
  "token_type": "Bearer",
  "expires_in": 604800
}

第 4 步:获取用户信息,建立本地会话

GET {平台地址}/oauth/userinfo
Authorization: Bearer {access_token}

响应(当前可用字段):

字段 类型 说明
id int 平台用户唯一 ID,建议作为你系统内的关联主键
username string 登录用户名(唯一)
nickname string 显示昵称
phone string 手机号
email string 企业邮箱
company string 企业名称

拿到用户信息后,典型做法:按 id 查你本地用户表 → 不存在则自动建档 → 签发你自己系统的会话(Cookie/JWT)。后续用户与你的系统交互,不再依赖平台。


4. 接入示例代码

Python(Flask)

import os, secrets, requests
from flask import Flask, redirect, request, session, abort

app = Flask(__name__)
app.secret_key = os.environ["FLASK_SECRET"]

BEATS = "https://beats.yun"          # 平台地址
CLIENT_ID = "beatsapp_xxxxxxxx"
CLIENT_SECRET = os.environ["BEATS_CLIENT_SECRET"]
REDIRECT_URI = "http://你的应用地址/callback"

@app.route("/login")
def login():
    state = secrets.token_urlsafe(16)
    session["oauth_state"] = state
    from urllib.parse import quote
    return redirect(
        f"{BEATS}/oauth/authorize?client_id={CLIENT_ID}"
        f"&redirect_uri={quote(REDIRECT_URI, safe='')}&state={state}"
    )

@app.route("/callback")
def callback():
    if request.args.get("state") != session.pop("oauth_state", None):
        abort(403, "state 校验失败")
    if request.args.get("error"):
        abort(403, "用户拒绝了授权")
    token = requests.post(f"{BEATS}/oauth/token", json={
        "grant_type": "authorization_code",
        "client_id": CLIENT_ID,
        "client_secret": CLIENT_SECRET,
        "code": request.args["code"],
        "redirect_uri": REDIRECT_URI,
    }, timeout=10).json()
    if "access_token" not in token:
        abort(502, f"换取令牌失败: {token.get('error')}")
    user = requests.get(f"{BEATS}/oauth/userinfo",
        headers={"Authorization": f"Bearer {token['access_token']}"},
        timeout=10).json()
    # TODO: 按 user["id"] 查/建本地用户,然后:
    session["uid"] = user["id"]
    session["nickname"] = user["nickname"]
    return redirect("/")

Node.js(Express)

const express = require("express");
const session = require("express-session");
const crypto = require("crypto");

const app = express();
app.use(session({ secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: false }));

const BEATS = "https://beats.yun";   // 平台地址
const CLIENT_ID = "beatsapp_xxxxxxxx";
const CLIENT_SECRET = process.env.BEATS_CLIENT_SECRET;
const REDIRECT_URI = "http://你的应用地址/callback";

app.get("/login", (req, res) => {
  const state = crypto.randomBytes(16).toString("hex");
  req.session.oauthState = state;
  res.redirect(`${BEATS}/oauth/authorize?client_id=${CLIENT_ID}` +
    `&redirect_uri=${encodeURIComponent(REDIRECT_URI)}&state=${state}`);
});

app.get("/callback", async (req, res) => {
  if (req.query.state !== req.session.oauthState) return res.status(403).send("state 校验失败");
  if (req.query.error) return res.status(403).send("用户拒绝了授权");
  const tokenRes = await fetch(`${BEATS}/oauth/token`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      grant_type: "authorization_code",
      client_id: CLIENT_ID,
      client_secret: CLIENT_SECRET,
      code: req.query.code,
      redirect_uri: REDIRECT_URI,
    }),
  }).then(r => r.json());
  if (!tokenRes.access_token) return res.status(502).send("换取令牌失败: " + tokenRes.error);
  const user = await fetch(`${BEATS}/oauth/userinfo`, {
    headers: { Authorization: `Bearer ${tokenRes.access_token}` },
  }).then(r => r.json());
  // TODO: 按 user.id 查/建本地用户
  req.session.user = user;
  res.redirect("/");
});

5. 错误码速查

场景 返回
client_id 或 redirect_uri 未登记 {"error":"无效的 client_id 或 redirect_uri(未注册)"}
用户拒绝授权 回调带 error=access_denied
授权码过期 / 已使用 / 不存在 {"error":"授权码无效或已过期"}
client_secret 错误 {"error":"客户端认证失败"}
access_token 无效或过期(userinfo) HTTP 401 {"error":"令牌无效或已过期"}

6. 安全规范(必读)

  1. client_secret 只存在于服务端。前端单页应用、小程序、App 客户端不得直接持有;纯前端应用应通过一个轻量后端代理完成第 3 步
  2. 必须校验 state,防 CSRF 登录劫持
  3. redirect_uri 精确匹配,平台已强制;你登记时不要使用过宽的路径
  4. 生产环境强制 HTTPS。平台已启用 HTTPS 证书,所有端点必须走 https://beats.yunhttp:// 会 301 跳转,但回调地址请直接登记 https:// 版本)
  5. access_token 有效期 7 天,过期后 userinfo 返回 401。v1 暂无 refresh token:令牌过期且你需要重新拉取用户资料时,引导用户重新走一次登录流程即可(已登录用户只需点一下「同意」,无密码输入)
  6. 建议只把 id 作为外键存库;手机号、邮箱等资料以平台为准,需要时实时拉取

7. FAQ

Q:用户在我的应用里能改密码吗? 不能。账号体系归平台统一管理,用户资料修改入口在平台门户。你的应用只做身份消费。

Q:用户在我的应用里"注册"过吗? 不需要。首次 Beats ID 登录时按 id 自动建档即可,对用户无感。

Q:同一用户在多个应用里是同一个人吗? 是。id 全平台唯一且恒定,可用于跨应用识别同一用户(注意遵守隐私合规要求)。

Q:怎么注销我应用的 client? 联系管理员,或管理员调用 DELETE /api/admin/oauth/clients/{client_id}。注销后该应用所有已签发令牌立即失效。

Q:以后会有 refresh token / 更多用户信息字段吗? 会在 v2 规划。接入时把 userinfo 解析写成容错方式(未知字段忽略),即可平滑升级。


本规范随平台 v1 上线发布,后续版本变更将在此文档追加 changelog。

Changelog