统一认证代理服务
Microcosm 统一认证代理服务
版本: 1.0.4 更新日期: 2026-01-11 状态: 生产环境运行中
概述
Microcosm 作为生态系统的核心平台,提供统一的认证代理服务。所有生态项目(如 Double Helix)的用户认证都必须通过 Microcosm API 进行,确保:
- 统一用户身份 - 一个账户访问所有生态项目
- 集中会话管理 - Microcosm 掌握所有活跃会话状态
- 安全合规 - 敏感凭证(如 Firebase Admin SDK)仅在 Microcosm 服务端使用
- 审计追踪 - 所有认证活动可追溯
架构设计
整体架构
整体架构:
用户浏览器访问各生态项目 (Double Helix, 其他生态项目), 各项目统一通过 Microcosm API 进行认证:
| 层级 | 组件 | 说明 |
|---|---|---|
| 用户层 | 用户浏览器 | 访问各生态项目 |
| 项目层 | Double Helix, 其他生态项目 | 前端/后端 |
| 认证层 | Microcosm API | OAuth 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 登录 |
| 4 | Microcosm | 用户 | 登录成功,重定向回生态项目 (带 code) |
| 5 | 用户 | 生态项目 | 用户浏览器自动跳转到回调 URL |
| 6 | 生态项目 | Microcosm | 用 code 换取 token |
| 7 | Microcosm | 生态项目 | 返回 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:
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>"
}
响应:
{
"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:
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>"
}
响应:
{
"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。
端点
POST https://microcosm.money/api/oauth/introspect
或集群内部:
POST http://user-service.microcosm.svc.cluster.local/oauth/introspect
请求
POST /oauth/introspect
Content-Type: application/json
{
"token": "<access_token>"
}
响应
Token 有效:
{
"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 无效或过期:
{
"active": false
}
字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
active | boolean | Token 是否有效 |
uid | string | Firebase UID |
email | string | 用户邮箱 |
display_name | string | 显示名称 |
role | string | 系统角色 (admin / user / agent) |
level | string | 用户级别 (recruit / prospect / miner) |
title | string | 职级头衔 (commander / pioneer / warden / admiral,可空) |
station_id | number | 所属 Station ID(Miner 才有) |
scope | string | 授权范围 |
client_id | string | 签发给哪个客户端 |
exp | number | 过期时间戳 (Unix) |
iat | number | 签发时间戳 (Unix) |
缓存策略
为减少对 Microcosm 的请求压力,生态项目可以缓存 introspect 结果:
| 策略 | TTL | 说明 |
|---|---|---|
| 短期缓存 | 60秒 | 推荐,平衡性能和实时性 |
| 不缓存 | - | 每次请求都验证,最安全 |
缓存键格式: token_introspect:{token_hash}
客户端注册
生态项目接入前需要在 Microcosm 注册为 OAuth 客户端。
注册信息
| 字段 | 示例 | 说明 |
|---|---|---|
client_id | doublehelix | 客户端唯一标识 |
client_secret | sk_live_xxx... | 客户端密钥(仅服务端使用) |
redirect_uris | ["https://doublehelix.money/auth/callback"] | 允许的回调地址列表 |
allowed_scopes | ["openid", "profile", "email", "mcc:read", "mining:read"] | 允许请求的权限 |
token_expiry | 3600 | Access Token 有效期(秒) |
已注册客户端
| 客户端 | client_id | 用途 |
|---|---|---|
| Double Helix | doublehelix | 加密货币交易平台 |
权限范围 (Scopes)
| Scope | 说明 | 返回信息 |
|---|---|---|
openid | OpenID Connect 基础 | uid |
profile | 用户资料 | display_name, avatar, role, level, title, station_id |
email | 邮箱信息 | email, email_verified |
mcc:read | MCC 余额权限 | 允许读取 MCC 余额和交易记录 |
mcd:read | MCD 余额权限 | 允许读取 MCD 余额和交易记录 |
mining:read | 铸造记录权限 | 允许读取铸造历史 |
字段说明:
role: 系统角色 (admin/user/agent)level: 用户级别 (recruit/prospect/miner)title: 职级头衔 (commander/pioneer/warden/admiral,可空)station_id: 所属 Station ID(Miner 才有)
API 端点汇总
公开端点 (无需认证)
| 端点 | 方法 | 说明 |
|---|---|---|
/login | GET | OAuth 登录页面 |
/api/oauth/token | POST | Token 交换/刷新 |
服务端端点 (需要 client_secret)
| 端点 | 方法 | 说明 |
|---|---|---|
/api/oauth/token | POST | 需要 client_secret |
/api/oauth/introspect | POST | Token 内省 |
用户端点 (需要 access_token)
| 端点 | 方法 | 说明 |
|---|---|---|
/api/users/profile | GET | 获取当前用户信息 |
/api/users/send-verification-email | POST | 发送邮箱验证 |
/api/auth/forgot-password | POST | 发送密码重置邮件 |
错误处理
OAuth 错误
| 错误码 | HTTP 状态 | 说明 |
|---|---|---|
invalid_request | 400 | 请求参数错误 |
invalid_client | 401 | client_id 或 client_secret 错误 |
invalid_grant | 400 | 授权码无效或已过期 |
invalid_token | 401 | access_token 无效 |
expired_token | 401 | access_token 已过期 |
insufficient_scope | 403 | 权限不足 |
错误响应格式
{
"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-key | Firebase API Key |
firebase-auth-domain | Firebase Auth Domain |
firebase-project-id | Firebase Project ID |
firebase-storage-bucket | Firebase Storage Bucket |
firebase-messaging-sender-id | Firebase Messaging Sender ID |
firebase-app-id | Firebase App ID |
firebase-measurement-id | Firebase Measurement ID |
构建命令 (无需手动传入环境变量):
cd /c/Microcosm/services/portal-service
gcloud builds submit --config=cloudbuild.yaml --region=asia-northeast1 .
查看/更新 Secrets:
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_total | Token 签发总数 |
oauth_introspect_total | 内省请求总数 |
oauth_introspect_latency | 内省请求延迟 |
前端集成注意事项 (2025-12-27)
问题背景
2025-12-27 Double Helix 项目遇到 OAuth 登录后立即登出的问题,根因是前端未正确解析 API 响应格式。
响应格式兼容
⚠️ 重要: 前端必须兼容两种 API 响应格式:
// 获取用户信息后的兼容处理
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
用户字段类型定义:
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 作为备选:
// 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 组件中使用认证状态时,注意避免闭包陷阱:
// ❌ 错误: 闭包可能捕获过时的 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_token | Microcosm Access Token |
mc_refresh_token | Microcosm Refresh Token |
mc_token_expires_at | Token 过期时间戳(毫秒) |
mc_user | 用户信息 JSON |
调试日志建议
在关键流程添加详细日志,便于排查问题:
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: false | Token 是否过期 | 使用 refresh_token 刷新 |
| 页面刷新后登出 | localStorage 是否正确存储 | 检查 AuthManager 存储逻辑 |
版本历史
| 版本 | 日期 | 变更 |
|---|---|---|
| 1.0.4 | 2026-01-11 | 与权威文档对齐:添加 level/title/station_id 字段,统一 role 定义为 admin/user/agent |
| 1.0.3 | 2026-01-07 | Firebase 环境变量迁移至 GCP Secret Manager,Cloud Build 自动注入 |
| 1.0.2 | 2025-12-28 | 实施"金库级"系统加固,确保 100% JSON 响应率,移除 HTML 错误页 |
| 1.0.1 | 2025-12-27 | 添加前端集成注意事项和故障排查指南 |
| 1.0.0 | 2025-12-26 | 初始版本,支持授权码流程和 Token 内省 |
联系方式
如有问题或需要注册新客户端,请联系 Microcosm 平台团队。