Skip to content

OpenID Connect 协议文档 ​

StarEcho 实现了标准的 OpenID Connect 1.0 协议(基于 OAuth 2.0 授权码模式),允许第三方应用接入节律星辰账号系统,实现「使用 StarEcho 登录」功能。

概览 ​

项目说明
协议OpenID Connect 1.0(Authorization Code Flow)
签名算法RS256
支持的 Scopeopenid、profile、email
支持的授权类型authorization_code、refresh_token
客户端认证client_secret_post、client_secret_basic、PKCE
Discovery 端点https://api.starecho.net/.well-known/openid-configuration

授权码流程时序图 ​

接入流程 ​

1. 创建应用 ​

登录 StarEcho,进入 个人中心 → 开发者 → 开发者应用,点击「创建应用」并填写:

字段说明
应用名称用户在授权页面看到的名称
应用描述简要说明应用功能
账号用途说明说明你的应用将如何使用 StarEcho 账号
回调地址OAuth 授权完成后的重定向 URI,须为 https://
Web Origin(CORS)允许从浏览器调用 StarEcho API 的精确 Origin,例如 https://app.example.com,不包含路径
移动端 URL Scheme可选,用于移动端原生应用回调,如 starecho,完整回调地址为 starecho://callback
应用主页可选,应用官网地址
应用图标可选,支持 JPG/PNG,建议 256×256

桌面端本地回调:http://127.0.0.1:{port}/callback 和 http://localhost:{port}/callback(端口 1024-65535)自动允许,无需配置。这符合 RFC 8252 规范,本地回环地址的流量不会离开本机。

提交后需等待管理员审核。审核通过后才可使用。

应用级 Web Origin ​

Web Origin 与 OAuth 回调地址用途不同:回调地址决定授权完成后可以跳转到哪里;Web Origin 决定哪些网页可以通过浏览器跨域调用 StarEcho API。应用所有者可在创建应用或应用信息页维护 webOrigins,无需平台管理员修改服务器环境变量。

  • 生产 Origin 必须使用 HTTPS,并且只能填写 协议 + 主机 + 端口,不能包含路径、查询参数、 Fragment、用户信息或通配符。
  • http://localhost:{port} 与 http://127.0.0.1:{port} 仅用于开发,必须明确填写 1024–65535 端口。
  • 每个应用最多登记 32 个 Origin。空列表表示不允许任何外部网页调用应用的浏览器 API。
  • /cf/api/public/{clientId}/... 与 /cf/http/public/{clientId}/... 会同时核对 URL 中的 clientId 和该应用的 Web Origin;其他需要授权的浏览器 API 仍会继续校验 Access Token、 Client ID 和权限。CORS 本身不是身份认证。
  • STARECHO_CORS_ORIGIN 仅用于平台自身的固定站点或运维 Origin,不应逐项登记开发者网站。

创建应用 POST /oauth/apps 与更新应用 PUT /oauth/apps/{clientId} 均接受:

json
{
  "webOrigins": [
    "https://app.example.com",
    "http://localhost:5100"
  ]
}

部署该能力时,必须先执行独立迁移 database_migrations/20260731-add-app-web-origins.sql,再部署新版平台包。迁移不会自动信任 任何现有网站;应用所有者需要显式保存 Web Origin。

2. 发起授权 ​

将用户重定向到授权页面:

GET https://starecho.net/oauth/authorize

查询参数:

参数必填说明
response_type✅固定为 code
client_id✅你的应用 Client ID
redirect_uri✅回调地址,必须与注册时填写的一致
scope✅请求的权限范围,空格分隔。例:openid profile email
state推荐随机字符串,用于防止 CSRF 攻击
nonce可选随机字符串,将包含在 id_token 中,用于防止重放攻击
code_challenge可选PKCE 挑战码(使用 PKCE 时必填)
code_challenge_method可选PKCE 方法:S256 或 plain

示例:

https://starecho.net/oauth/authorize?
  response_type=code&
  client_id=YOUR_CLIENT_ID&
  redirect_uri=https://your-app.com/callback&
  scope=openid profile email&
  state=random_state_string

用户将看到授权页面,显示你的应用信息和请求的权限。用户确认授权后,浏览器将重定向到你的回调地址:

https://your-app.com/callback?code=AUTHORIZATION_CODE&state=random_state_string

3. 交换 Token ​

使用授权码向 Token 端点请求令牌。支持 application/x-www-form-urlencoded(OAuth 2.0 标准格式)和 application/json 两种请求格式。

POST https://api.starecho.net/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=https%3A%2F%2Fyour-app.com%2Fcallback&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET

也可以使用 JSON 格式(Content-Type: application/json):

json
{
  "grant_type": "authorization_code",
  "code": "AUTHORIZATION_CODE",
  "redirect_uri": "https://your-app.com/callback",
  "client_id": "YOUR_CLIENT_ID",
  "client_secret": "YOUR_CLIENT_SECRET"
}

TIP

大多数 OAuth 2.0 客户端库默认使用 application/x-www-form-urlencoded 格式,如果你使用的框架自动处理 Token 交换,通常无需手动指定 Content-Type。

也支持 HTTP Basic 认证(client_secret_basic),此时无需在请求体中传递 client_id 和 client_secret:

POST https://api.starecho.net/oauth/token
Authorization: Basic base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=https%3A%2F%2Fyour-app.com%2Fcallback

成功响应:

json
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "id_token": "eyJhbGciOiJSUzI1NiIs...",
  "refresh_token": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4...",
  "scope": "openid profile email"
}
字段说明
access_token访问令牌(RS256 JWT,有效期 1 小时)
token_type固定为 Bearer
expires_in令牌有效期(秒)
id_token身份令牌(仅当 scope 包含 openid 时返回)
refresh_token刷新令牌(有效期 30 天,每次使用后轮换)
scope实际授权的权限范围

4. 获取用户信息 ​

使用 access_token 请求用户信息:

GET https://api.starecho.net/oauth/userinfo
Authorization: Bearer ACCESS_TOKEN

成功响应:

json
{
  "sub": "100001",
  "name": "StarUser",
  "email": "user@example.com",
  "gender": "male"
}
字段Scope说明
sub始终返回用户唯一标识(uid)
nameprofile用户昵称
genderprofile性别(male / female)
emailemail邮箱地址

5. 刷新令牌 ​

当 access_token 过期后,你可以使用 refresh_token 获取一组新的令牌,而无需用户重新授权。这遵循 OIDC Core 1.0 §12 规范。

POST https://api.starecho.net/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=YOUR_REFRESH_TOKEN&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET

也可以使用 JSON 格式:

json
{
  "grant_type": "refresh_token",
  "refresh_token": "YOUR_REFRESH_TOKEN",
  "client_id": "YOUR_CLIENT_ID",
  "client_secret": "YOUR_CLIENT_SECRET"
}

PKCE 应用

如果你的应用使用 PKCE 认证(无 client_secret),只需传递 client_id 即可,无需 client_secret。

请求参数:

参数必填说明
grant_type✅固定为 refresh_token
refresh_token✅之前获取的刷新令牌
client_id✅你的应用 Client ID
client_secret条件必填非 PKCE 应用必须提供
scope可选可以请求原始 scope 的子集(缩小权限范围)

成功响应:

json
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "id_token": "eyJhbGciOiJSUzI1NiIs...",
  "refresh_token": "NEW_REFRESH_TOKEN",
  "scope": "openid profile email"
}

令牌轮换

每次使用 refresh_token 后,服务器将返回一个新的 refresh_token,原 refresh_token 立即失效。请务必保存并使用最新的 refresh_token。

如果检测到已失效的 refresh_token 被再次使用(疑似令牌泄露),服务器将自动撤销该用户在该应用下的所有刷新令牌,以保护账号安全。

刷新令牌属性:

属性值
有效期30 天
轮换策略每次使用后签发新令牌,旧令牌失效
重放检测检测到旧令牌重用时,撤销该应用的所有令牌
Scope 缩小可在刷新时请求原始 scope 的子集

PKCE 支持 ​

对于无法安全存储 client_secret 的客户端(如原生应用、SPA),可以使用 PKCE(Proof Key for Code Exchange) 代替客户端密钥进行认证。

流程 ​

  1. 生成 code_verifier:43-128 位的随机字符串
  2. 计算 code_challenge:BASE64URL(SHA256(code_verifier))
  3. 发起授权时携带 code_challenge 和 code_challenge_method=S256
  4. 交换 Token 时携带 code_verifier(不需要 client_secret)

Token 请求示例(PKCE):

json
{
  "grant_type": "authorization_code",
  "code": "AUTHORIZATION_CODE",
  "redirect_uri": "https://your-app.com/callback",
  "client_id": "YOUR_CLIENT_ID",
  "code_verifier": "YOUR_CODE_VERIFIER"
}

原生应用接入示例 ​

桌面端(本地回环 + PKCE):

// 1. 在本地启动 HTTP 服务器监听随机端口
// 2. 构造授权 URL
https://starecho.net/oauth/authorize?
  response_type=code&
  client_id=YOUR_CLIENT_ID&
  redirect_uri=http://127.0.0.1:12345/callback&
  scope=openid profile email&
  state=random_state&
  code_challenge=CHALLENGE&
  code_challenge_method=S256

// 3. 打开系统浏览器让用户授权
// 4. 用户授权后浏览器跳转到 http://127.0.0.1:12345/callback?code=xxx
// 5. 本地服务器接收授权码,用 PKCE 交换 Token

移动端(自定义 URL Scheme + PKCE):

// 前提:在应用配置中注册了 scheme“starecho”
https://starecho.net/oauth/authorize?
  response_type=code&
  client_id=YOUR_CLIENT_ID&
  redirect_uri=starecho://callback&
  scope=openid profile email&
  state=random_state&
  code_challenge=CHALLENGE&
  code_challenge_method=S256

// 用户授权后系统通过 Deep Link 唤起应用:
// starecho://callback?code=AUTHORIZATION_CODE&state=random_state

Scope 说明 ​

Scope包含的信息
openid返回 id_token,包含 sub(用户 ID)
profile用户昵称(name)、性别(gender)
email用户邮箱(email)

ID Token ​

当请求的 scope 包含 openid 时,Token 响应中将包含 id_token。这是一个 RS256 签名的 JWT,包含以下 Claims:

Claim说明
sub用户唯一标识(uid)
aud你的 Client ID
iss签发者 URL
iat签发时间(Unix 时间戳)
exp过期时间(Unix 时间戳)
nonce请求中传入的 nonce(如有)
name用户昵称(scope 含 profile 时)
email邮箱地址(scope 含 email 时)
gender性别(scope 含 profile 时)

可通过 JWKS 端点 获取公钥来验证 id_token 的签名。

端点参考 ​

端点方法URL说明
DiscoveryGET/.well-known/openid-configurationOIDC 服务发现
JWKSGET/oauth/jwksJSON Web Key Set 公钥
授权GET/oauth/authorize授权页面(前端)
授权检查GET/oauth/authorize/check验证授权参数
TokenPOST/oauth/token授权码换取令牌 / 刷新令牌
UserInfoGET/oauth/userinfo获取用户信息

基础 URL

  • 前端页面:https://starecho.net
  • API 端点:https://api.starecho.net

错误处理 ​

授权错误 ​

当授权失败时,用户将被重定向到回调地址并附带错误参数:

https://your-app.com/callback?error=access_denied&state=random_state_string
错误码说明
access_denied用户拒绝授权
invalid_client应用不存在
client_not_approved应用未通过审核
invalid_redirect_uri回调地址不匹配

Token 错误 ​

Token 端点返回 JSON 格式的错误:

json
{
  "error": "invalid_grant",
  "error_description": "Authorization code expired"
}
错误码说明
unsupported_grant_type不支持的授权类型
invalid_grant授权码无效、已过期或已使用;或刷新令牌无效、已过期或已被撤销
invalid_clientClient ID 或 Secret 错误
invalid_scope请求的 scope 超出原始授权范围(仅刷新令牌)

安全建议 ​

  1. 始终使用 HTTPS — Web 应用的回调地址必须使用 https:// 协议
  2. 验证 state — 对比授权请求和回调中的 state 参数,防止 CSRF 攻击
  3. 妥善保管 client_secret — 切勿在前端代码、版本控制或日志中暴露密钥
  4. 使用 PKCE — 原生应用、SPA 等无法安全存储密钥的应用应使用 PKCE,强烈建议所有原生应用都启用 PKCE
  5. 验证 id_token — 校验签名、issuer、audience 和过期时间
  6. 最小权限原则 — 仅请求所需的 scope

原生应用回调地址说明 ​

平台回调地址格式说明
Web 应用https://your-app.com/callback需在应用配置中注册
桌面端http://127.0.0.1:{port}/callback自动允许,端口 1024-65535
桌面端http://localhost:{port}/callback自动允许,端口 1024-65535
移动端{scheme}://callback需在应用配置中注册 scheme

示例代码 ​

JavaScript / Node.js ​

javascript
// 1. 构造授权 URL 并重定向
const params = new URLSearchParams({
  response_type: 'code',
  client_id: 'YOUR_CLIENT_ID',
  redirect_uri: 'https://your-app.com/callback',
  scope: 'openid profile email',
  state: crypto.randomUUID(),
})
window.location.href = `https://starecho.net/oauth/authorize?${params}`

// 2. 回调处理:用授权码换取 Token
const tokenRes = await fetch('https://api.starecho.net/oauth/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    grant_type: 'authorization_code',
    code: authorizationCode,
    redirect_uri: 'https://your-app.com/callback',
    client_id: 'YOUR_CLIENT_ID',
    client_secret: 'YOUR_CLIENT_SECRET',
  }),
})
const tokens = await tokenRes.json()

// 3. 获取用户信息
const userRes = await fetch('https://api.starecho.net/oauth/userinfo', {
  headers: { Authorization: `Bearer ${tokens.access_token}` },
})
const user = await userRes.json()
console.log(user.name, user.email)

Python ​

python
import requests

# 交换 Token
token_res = requests.post('https://api.starecho.net/oauth/token', json={
    'grant_type': 'authorization_code',
    'code': authorization_code,
    'redirect_uri': 'https://your-app.com/callback',
    'client_id': 'YOUR_CLIENT_ID',
    'client_secret': 'YOUR_CLIENT_SECRET',
})
tokens = token_res.json()

# 获取用户信息
user_res = requests.get('https://api.starecho.net/oauth/userinfo', headers={
    'Authorization': f'Bearer {tokens["access_token"]}'
})
user = user_res.json()
print(user['name'], user['email'])

© 2026 StarEcho