开发者指南

统一认证代理服务

Microcosm 统一认证代理服务

版本: 1.0.4 更新日期: 2026-01-11 状态: 生产环境运行中

概述

Microcosm 作为生态系统的核心平台,提供统一的认证代理服务。所有生态项目(如 Double Helix)的用户认证都必须通过 Microcosm API 进行,确保:

  1. 统一用户身份 - 一个账户访问所有生态项目
  2. 集中会话管理 - Microcosm 掌握所有活跃会话状态
  3. 安全合规 - 敏感凭证(如 Firebase Admin SDK)仅在 Microcosm 服务端使用
  4. 审计追踪 - 所有认证活动可追溯

架构设计

整体架构

整体架构:

用户浏览器访问各生态项目 (Double Helix, 其他生态项目), 各项目统一通过 Microcosm API 进行认证:

层级组件说明
用户层用户浏览器访问各生态项目
项目层Double Helix, 其他生态项目前端/后端
认证层Microcosm APIOAuth 2.0 Endpoints, Token Introspect, User Management
底层Firebase Auth仅 Microcosm 服务端访问

数据流向: 用户浏览器 -> 生态项目 (前端/后端) -> Microcosm API -> Firebase Auth

核心原则

原则说明
认证代理生态项目不直接使用 Firebase SDK,所有认证通过 Microcosm API
Token 中心化Microcosm 签发和验证所有 access_token
会话可控Microcosm 可随时撤销任何用户的会话
最小权限生态项目只获得必要的用户信息

OAuth 2.0 认证流程

授权码流程 (Authorization Code Flow)

这是推荐的认证方式,适用于有服务端的 Web 应用。

授权码流程时序:

步骤发起方接收方操作
1用户生态项目点击登录
2生态项目用户重定向到 Microcosm 登录页
3用户Microcosm用户在 Microcosm 登录
4Microcosm用户登录成功,重定向回生态项目 (带 code)
5用户生态项目用户浏览器自动跳转到回调 URL
6生态项目Microcosm用 code 换取 token
7Microcosm生态项目返回 access_token + refresh_token
8生态项目用户登录成功,返回应用

步骤详解

1. 构建授权 URL

生态项目构建授权 URL 并重定向用户:

GET https://microcosm.money/login?oauth=true &response_type=code &client_id=doublehelix &redirect_uri=https://doublehelix.money/auth/callback &scope=openid profile email mcc:read &state=<random_state>
参数必填说明
oauth固定为 true,触发 OAuth 流程
response_type固定为 code
client_id在 Microcosm 注册的客户端 ID
redirect_uri授权后回调地址,必须与注册时一致
scope请求的权限范围,空格分隔
state推荐随机字符串,防止 CSRF 攻击
signup设为 true 显示注册界面

2. 用户在 Microcosm 登录

用户在 Microcosm 登录页完成身份验证(邮箱密码或第三方登录)。

3. 授权码回调

登录成功后,Microcosm 重定向到 redirect_uri

GET https://doublehelix.money/auth/callback ?code=<authorization_code> &state=<state>

4. 用授权码换取 Token

生态项目服务端使用授权码换取 Token:

http
POST https://microcosm.money/api/oauth/token
Content-Type: application/json

{
  "grant_type": "authorization_code",
  "code": "<authorization_code>",
  "redirect_uri": "https://doublehelix.money/auth/callback",
  "client_id": "doublehelix",
  "client_secret": "<client_secret>"
}

响应:

json
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "openid profile email mcc:read",
  "user": {
    "uid": "abc123",
    "email": "user@example.com",
    "display_name": "User Name",
    "role": "user",
    "level": "miner",
    "title": "commander",
    "station_id": 1
  }
}

用户字段说明:

  • role: 系统角色 (admin | user | agent)
  • level: 用户级别 (recruit | prospect | miner)
  • title: 职级头衔 (commander | pioneer | warden | admiral,可空)

Token 刷新流程

Access Token 默认 1 小时过期,使用 Refresh Token 获取新 Token:

http
POST https://microcosm.money/api/oauth/token
Content-Type: application/json

{
  "grant_type": "refresh_token",
  "refresh_token": "<refresh_token>",
  "client_id": "doublehelix",
  "client_secret": "<client_secret>"
}

响应:

json
{
  "access_token": "<new_access_token>",
  "refresh_token": "<new_refresh_token>",
  "token_type": "Bearer",
  "expires_in": 3600
}

注意: 每次刷新都会返回新的 refresh_token,旧的立即失效(轮换机制)。


Token 内省 API (Introspect)

生态项目后端验证 access_token 时,必须调用 Microcosm 的 Token 内省 API。

端点

http
POST https://microcosm.money/api/oauth/introspect

或集群内部:

http
POST http://user-service.microcosm.svc.cluster.local/oauth/introspect

请求

http
POST /oauth/introspect
Content-Type: application/json

{
  "token": "<access_token>"
}

响应

Token 有效:

json
{
  "active": true,
  "uid": "abc123",
  "email": "user@example.com",
  "display_name": "User Name",
  "role": "user",
  "level": "miner",
  "title": "commander",
  "station_id": 1,
  "scope": "openid profile email mcc:read",
  "client_id": "doublehelix",
  "exp": 1703577600,
  "iat": 1703574000
}

Token 无效或过期:

json
{
  "active": false
}

字段说明

字段类型说明
activebooleanToken 是否有效
uidstringFirebase UID
emailstring用户邮箱
display_namestring显示名称
rolestring系统角色 (admin / user / agent)
levelstring用户级别 (recruit / prospect / miner)
titlestring职级头衔 (commander / pioneer / warden / admiral,可空)
station_idnumber所属 Station ID(Miner 才有)
scopestring授权范围
client_idstring签发给哪个客户端
expnumber过期时间戳 (Unix)
iatnumber签发时间戳 (Unix)

缓存策略

为减少对 Microcosm 的请求压力,生态项目可以缓存 introspect 结果:

策略TTL说明
短期缓存60秒推荐,平衡性能和实时性
不缓存-每次请求都验证,最安全

缓存键格式: token_introspect:{token_hash}


客户端注册

生态项目接入前需要在 Microcosm 注册为 OAuth 客户端。

注册信息

字段示例说明
client_iddoublehelix客户端唯一标识
client_secretsk_live_xxx...客户端密钥(仅服务端使用)
redirect_uris["https://doublehelix.money/auth/callback"]允许的回调地址列表
allowed_scopes["openid", "profile", "email", "mcc:read", "mining:read"]允许请求的权限
token_expiry3600Access Token 有效期(秒)

已注册客户端

客户端client_id用途
Double Helixdoublehelix加密货币交易平台

权限范围 (Scopes)

Scope说明返回信息
openidOpenID Connect 基础uid
profile用户资料display_name, avatar, role, level, title, station_id
email邮箱信息email, email_verified
mcc:readMCC 余额权限允许读取 MCC 余额和交易记录
mcd:readMCD 余额权限允许读取 MCD 余额和交易记录
mining:read铸造记录权限允许读取铸造历史

字段说明:

  • role: 系统角色 (admin / user / agent)
  • level: 用户级别 (recruit / prospect / miner)
  • title: 职级头衔 (commander / pioneer / warden / admiral,可空)
  • station_id: 所属 Station ID(Miner 才有)

API 端点汇总

公开端点 (无需认证)

端点方法说明
/loginGETOAuth 登录页面
/api/oauth/tokenPOSTToken 交换/刷新

服务端端点 (需要 client_secret)

端点方法说明
/api/oauth/tokenPOST需要 client_secret
/api/oauth/introspectPOSTToken 内省

用户端点 (需要 access_token)

端点方法说明
/api/users/profileGET获取当前用户信息
/api/users/send-verification-emailPOST发送邮箱验证
/api/auth/forgot-passwordPOST发送密码重置邮件

错误处理

OAuth 错误

错误码HTTP 状态说明
invalid_request400请求参数错误
invalid_client401client_id 或 client_secret 错误
invalid_grant400授权码无效或已过期
invalid_token401access_token 无效
expired_token401access_token 已过期
insufficient_scope403权限不足

错误响应格式

json
 {
   "success": false,
   "error": "invalid_token",
   "message": "The access token has expired"
 }

注意: 严禁返回 HTML 报错。所有后端服务已加固全局异常拦截器,确保即使在发生非预期错误(如数据库掉线)时,前端收到的依然是结构化的 JSON 数据。


安全最佳实践

客户端密钥保护

  • client_secret 只能在服务端使用,绝不能暴露给前端
  • 使用环境变量或 Secret Manager 存储
  • 定期轮换密钥

Firebase 环境变量管理 (2026-01-07 升级)

⚠️ 重要: Next.js 的 NEXT_PUBLIC_* 环境变量必须在构建时注入,不能在运行时通过 K8s 环境变量设置。

工业标准方案: 所有 Firebase 客户端配置存储在 GCP Secret Manager,Cloud Build 构建时自动注入。

Secret 名称说明
firebase-api-keyFirebase API Key
firebase-auth-domainFirebase Auth Domain
firebase-project-idFirebase Project ID
firebase-storage-bucketFirebase Storage Bucket
firebase-messaging-sender-idFirebase Messaging Sender ID
firebase-app-idFirebase App ID
firebase-measurement-idFirebase Measurement ID

构建命令 (无需手动传入环境变量):

bash
cd /c/Microcosm/services/portal-service
gcloud builds submit --config=cloudbuild.yaml --region=asia-northeast1 .

查看/更新 Secrets:

bash
gcloud secrets versions access latest --secret=firebase-api-key
echo "NEW_VALUE" | gcloud secrets versions add firebase-api-key --data-file=-

Token 存储

环境存储方式说明
浏览器localStorage仅存储 access_token
服务端内存/Redis可存储 refresh_token

HTTPS 强制

  • 所有 OAuth 端点必须使用 HTTPS
  • 回调地址必须是 HTTPS(开发环境除外)

State 参数

  • 每次授权请求生成随机 state
  • 回调时验证 state 是否匹配
  • 防止 CSRF 攻击

监控与审计

日志记录

Microcosm 记录所有认证相关活动:

事件记录内容
登录成功uid, client_id, IP, 时间
登录失败email, 失败原因, IP, 时间
Token 签发uid, client_id, scope, 时间
Token 刷新uid, client_id, 时间
Token 内省token_hash, client_id, 结果, 时间

指标监控

指标说明
oauth_login_total登录总次数
oauth_token_issued_totalToken 签发总数
oauth_introspect_total内省请求总数
oauth_introspect_latency内省请求延迟

前端集成注意事项 (2025-12-27)

问题背景

2025-12-27 Double Helix 项目遇到 OAuth 登录后立即登出的问题,根因是前端未正确解析 API 响应格式。

响应格式兼容

⚠️ 重要: 前端必须兼容两种 API 响应格式:

typescript
// 获取用户信息后的兼容处理
const responseData = await response.json()

// 格式1 (直接返回): {uid, email, role, level, title, station_id}
// 格式2 (包装返回): {success: true, user: {uid, email, role, level, title, station_id}}
const userData = responseData.user || responseData

// 使用用户数据
const { uid, role, level, title, station_id } = userData

用户字段类型定义:

typescript
interface User {
  uid: string
  email: string
  displayName?: string
  role: 'admin' | 'user' | 'agent'                                    // 系统角色
  level: 'recruit' | 'prospect' | 'miner'                             // 用户级别
  title?: 'commander' | 'pioneer' | 'warden' | 'admiral' | null       // 职级头衔
  stationId?: number                                                  // 所属 Station ID
}

备选数据源

如果 introspect API 返回的 uid 缺失,可使用 token exchange 返回的 user_id 作为备选:

typescript
// Token 交换响应包含 user_id
const tokenData = await tokenExchange(code)
// { access_token, refresh_token, expires_in, user_id }

// 获取用户信息
const userData = await fetchUserProfile(tokenData.access_token)

// 备选逻辑
const uid = userData.uid || tokenData.user_id

React 状态时序问题

在 React 组件中使用认证状态时,注意避免闭包陷阱:

typescript
// ❌ 错误: 闭包可能捕获过时的 state
const fetchData = async () => {
  if (!user?.uid) return  // user 可能是旧值
}

// ✅ 正确: 将依赖值作为参数传递
const fetchData = async (uid: string) => {
  if (!uid) return
}

useEffect(() => {
  if (!user?.uid) return
  fetchData(user.uid)  // 直接传递当前值
}, [user])

localStorage 键名规范

生态项目应使用统一的 localStorage 键名前缀:

说明
mc_access_tokenMicrocosm Access Token
mc_refresh_tokenMicrocosm Refresh Token
mc_token_expires_atToken 过期时间戳(毫秒)
mc_user用户信息 JSON

调试日志建议

在关键流程添加详细日志,便于排查问题:

typescript
console.log("[OAuth Callback] Token exchange success, access_token length:", token.length)
console.log("[OAuth Callback] Profile response:", responseData)
console.log("[OAuth Callback] Extracted user data:", userData)
console.log("[OAuth Callback] uid =", userData.uid)

故障排查清单

症状检查点解决方案
登录后立即登出user.uid 是否 undefined检查响应格式解析
Token 交换失败网络请求是否成功检查 Microcosm 服务可达性
introspect 返回 active: falseToken 是否过期使用 refresh_token 刷新
页面刷新后登出localStorage 是否正确存储检查 AuthManager 存储逻辑

版本历史

版本日期变更
1.0.42026-01-11与权威文档对齐:添加 level/title/station_id 字段,统一 role 定义为 admin/user/agent
1.0.32026-01-07Firebase 环境变量迁移至 GCP Secret Manager,Cloud Build 自动注入
1.0.22025-12-28实施"金库级"系统加固,确保 100% JSON 响应率,移除 HTML 错误页
1.0.12025-12-27添加前端集成注意事项和故障排查指南
1.0.02025-12-26初始版本,支持授权码流程和 Token 内省

联系方式

如有问题或需要注册新客户端,请联系 Microcosm 平台团队。

询问 AI
询问 AI