KenOAuth + JWT 迁移总计划
- 状态:执行中
- 涉及仓库:
C:\工程\KenApi(GitData)、C:\工程\KenOAuth、C:\工程\KenAdmin - 目标:GitData 的「用户认证」「会话」「space 身份/加入流程」由 KenOAuth 唯一持有;GitData 用 JWT 验签,不再维护任何服务端会话存储
- 前置结论:空库对接,不做存量数据迁移、不兼容旧口令
- 本文取代旧版计划,是唯一执行依据
0. 执行进度(2026-10-24)
| 批次 | 状态 | 已落地内容 |
|---|---|---|
| B0.5 | ✅ | 删除 app/adminhost/lib/admin/lib/admin-rpc;[internal] service_key + x-gitdata-service-key 守卫 |
| B0 | ✅ | 新增 lib/kenauth(proto + 客户端 + 拦截器);[oauth] 配置;compose nats/oauth;Helm oauth.yaml;Dockerfile 清理 |
| B1 | ✅ | users.uid 及全部用户外键改为 i64;jwt::Session::uid() -> i64;OpenAPI 与 web 类型重新生成 |
| B2.0 | ✅ | lib/jwt:EdDSA + JWKS 缓存、access/service claims、Session 提取器、cookie、guard、share 兼容层 |
| B2.1 | ✅ | webapi 注入 JwtVerifier + 全局 revalidate(Redis 60s → GetSession) |
| B2.2 | ✅ | GET /auth/login、GET /auth/callback、POST /auth/refresh(Redis 单飞锁)、POST /auth/logout |
| B2.3 | ✅ | 357 个文件 session_layer → jwt;删除 8 处 impl SessionGuard;api_error! 改 ::jwt::SessionError |
| B2.4 | ✅ | 删除旧 register/login/2FA/PoW/邮箱/口令/AccessKey/SSHKey/设备/注销的 handler 与 service |
| B2.5 | ✅ | 删除 lib/session、lib/session-layer、[session] 配置与依赖;SameSite 并入 [oauth] |
| B2.6 | ✅ | app/im 改 KenOAuth service token(TokenService)+ IntrospectToken;/auth/jwt-request 改 IssueServiceToken;删除 export::jwt 与 auth.chat_jwt_sec |
| B2.7 | ✅ | 前端 401 token_expired 单飞 refresh + 登录跳转;删除 6 个认证页面与旧 PoW wasm |
| B2.8 | ✅ | Helm/compose/NOTES/RUST_LOG 已改;/readyz 增加 KenOAuth gRPC 健康探针;JWKS 拉取/刷新失败改为 error!(target gitdata::jwks)供告警 |
| B3 | ✅ | verify_access_key、SSH 公钥校验改 gRPC;删除 user_access_keys/user_ssh_keys 及会话/设备/恢复码/注销/邮箱验证码模型 |
| K1 | ✅ | KenOAuth users.bot/system、EnsureBotUser;Principal/UserPublic/AuthorityUser 透出 bot/system |
| K4 | ✅ | 表结构 + JoinRule/JoinRequest RPC + EnsureBotMember(支持 role_keys/role_ids);CreateOrganization/UpdateOrganization 支持 join 字段;邀请支持 email 绑定与多角色(role_ids) |
| B4 | ✅ | space/member/ban/group/invite/join-rule/join-request 全部写透 KenOAuth;apply_space_join_rules 经 EnsureBotMember(role_ids) 投影;权限判定改 CheckPermissions(300s Redis 投影) |
| B5 | ✅ | /internal 服务密钥守卫复核;未使用依赖清理;openapi.json + web 客户端重新生成;openapi --check、web build/lint 通过;全仓代码无 session_layer/lib/session/admin_rpc 引用 |
当前
cargo check --workspace、KenOAuthcargo check与cargo run -p openapi -- --check均通过;bun run --cwd web build通过。已知待收尾:
- B4.3 的物理删表改为「保留本地表作为投影缓存」:读路径与
dir覆盖仍依赖这些表,事实源已全部在 KenOAuth。- K2/K3/K5(协议接受、MFA 注册/恢复码、账号删除)与 §9 中标注「代理」的自助端点(2FA、session、access key、ssh key、deletion)尚未落地;前端对应设置分区暂不可用,属于后续批次。
- 邮件邀请:KenOAuth 负责 email 绑定与兑换校验,邮件投递仍由 GitData 发送(链接指向 GitData SPA)。
user_emails投影表暂留(单邮箱模型待 K5 完成后删除)。
1. 目标与非目标
1.1 目标
- KenOAuth 成为唯一认证源:账号、口令、邮箱、TOTP、会话、Access Token、SSH 公钥、协议接受。
- KenOAuth 成为唯一组织事实源:组织、成员、角色、角色权限、邀请、封禁、加入规则、加入申请。
- GitData 浏览器侧改为 OIDC 授权码 + PKCE,cookie 中只放 KenOAuth 颁发的 access token。
- 删除
lib/session与lib/session-layer,新lib/jwt只做 EdDSA/JWKS 验签。 - GitData 用户主键统一为
i64,与auth.common.v1.Principal.user_id对齐。 - KenShell(Git 数据面)通过 gRPC 校验 Access Key / SSH 公钥。
- 删除 GitData 自带管理后台,admin 用户与 RBAC 完全由
C:\工程\KenAdmin管理。 /api/v1/internal/*改用服务密钥鉴权。
1.2 非目标
- 不迁移存量用户、口令、会话、Access Key、space 数据。
- 不把
rooms、dir、目录覆盖、space 图片、space_warn迁入 KenOAuth。 - 不把仓库、issue、PR、CI、chat 消息迁入 KenOAuth。
- GitData 已移除
mails服务,非认证邮件(issue/PR/chat/space 邀请)改走 KenOAuth 内部EmailService.SendEmail。 - 不在本期替换 CI 的
ci_job token。 - 不在本期实现 WebAuthn / SMS 多因子(KenOAuth 表结构已就绪)。
- 不在本期把 GitData 的资源管理能力迁入 KenAdmin(见 §16 能力缺口)。
2. 决策记录
| 编号 | 决策 | 结论 | 影响 |
|---|---|---|---|
| D1 | 用户主键 | GitData 全量 Uuid → i64 | 87 个 model、全部用户外键、OpenAPI、前端类型 |
| D2 | 浏览器认证 | OIDC 授权码 + PKCE;cookie 存 KenOAuth access token | 删除 GitData 登录/注册/重置/2FA 页面 |
| D3 | Space 归属 | 身份事实 + 加入流程 → KenOAuth | 新建 2 张表 + 10 个 RPC |
| D4 | 数据库 | 独立 auth 库 | GitData 摘掉跨库外键,保留投影表 |
| D5 | 存量数据 | 空库对接 | 不做迁移脚本、不做口令兼容 |
| D6 | 邮件邀请 | KenOAuth 生成 token 并直接发信 | invite 增 email、新增模板 |
| D7 | Bot 账号 | KenOAuth 建 bot 账号 | users 增 bot/system、新增 2 个 RPC |
| D8 | 多邮箱 | 改为单邮箱 | 删除 user_emails 及 6 个接口 |
| D9 | 组织 ID | 保持 UUID,与 spaces.id 1:1 | 资源表外键不变 |
| D10 | 权限判定 | 角色权限同名映射 + CheckPermission(300s 缓存) | 删除 space_group_permissions 等 4 张表 |
| D11 | 会话实现 | 删除 lib/session / lib/session-layer,新建 lib/jwt | 412 个文件 import 改名 |
| D12 | 令牌校验 | 只验签 KenOAuth 令牌(EdDSA + JWKS),GitData 不自签 | 不再需要会话签名密钥 |
| D13 | 续期 | refresh token 放独立 HttpOnly cookie | 新增 /auth/refresh + Redis 单飞锁 |
| D14 | access TTL | 保持 KenOAuth 默认 600s | 约每 9 分钟刷新一次 |
| D15 | Admin | 完全由 C:\工程\KenAdmin 管理 | 删除 adminhost / lib/admin / lib/admin-rpc |
| D16 | /internal | 服务密钥头 x-gitdata-service-key | 新增 [internal] service_key |
| D17 | 聊天令牌 | KenOAuth TokenService.IssueServiceToken(audience=CHAT) | 删除 auth.chat_jwt_sec 与 export::jwt |
3. 目标架构
3.1 组件图
┌──────────────────────────────┐
│ Browser SPA │
│ (web, Vite/React) │
└──────────┬───────────────────┘
│ cookie: gitdata_session / gitdata_refresh
▼
┌──────────────────────────────┐
│ KenOAuth (oauth) │
│ HTTP 18081 / gRPC 18082 │
│ 账号 / 会话 / 令牌 / 组织 │
│ PostgreSQL(auth) + Redis │
│ + NATS + SMTP │
└──────▲──────────────▲────────┘
OIDC code/token │ │ gRPC + x-rpc-key
│ │
┌───────────────────────────┴──┐ ┌───────┴───────────────────────┐
│ apihost (:3000 / dev 4081) │ │ KenShell (gRPC 50091) │
│ - OIDC RP / refresh │ │ - smart HTTP / SSH │
│ - lib/jwt 验签 │ └───────────────────────────────┘
│ - 业务 REST / SPA │
│ - 本地投影读写 │
└──────────┬───────────────────┘
│ 本地投影:users / spaces / space_members
▼
PostgreSQL(gitdata) + Redis + RustFS + Meilisearch
▲
│
synchost / searchhost / im / webhook
3.2 令牌流转
登录:
浏览器 → /api/v1/auth/login → 302 KenOAuth /api/v1/oauth/authorize (PKCE)
→ /api/v1/auth/callback?code
→ 后端 POST /api/v1/oauth/token
→ 验 id_token(JWKS + nonce)
→ Set-Cookie:
gitdata_session = access_token (600s, Path=/)
gitdata_refresh = refresh_token (30d, Path=/api/v1/auth)
请求:
cookie → lib/jwt 验签(EdDSA + JWKS)→ AccessClaims{sub, sid, sv, username}
→ revalidate(Redis 60s → AuthorityService.GetSession)
→ handler require_uid
过期:
API 401 token_expired
→ POST /api/v1/auth/refresh(Redis 单飞锁)
→ POST /api/v1/oauth/token (refresh_token, 轮换)
→ 覆盖两个 cookie → 重放原请求
3.3 服务边界
| 关注点 | 归属 | 访问方式 |
|---|---|---|
| 凭据是否正确 | KenOAuth | IdentityService.Authenticate |
| 账号是否可用 | KenOAuth | IdentityService.GetUserStatus / AuthorityService.GetUser |
| 会话是否存活 | KenOAuth | AuthorityService.GetSession |
| 组织内能否做某动作 | KenOAuth | OrganizationService.CheckPermission |
| 令牌签名 | KenOAuth | /.well-known/jwks.json |
| 仓库/房间/工单准入 | GitData | 本地表 + space_members 投影 |
| 平台管理 | KenAdmin | 独立系统,与 GitData 无用户体系耦合 |
4. 数据归属
4.1 KenOAuth 唯一持有
users、user_sessions、user_access_tokens、user_ssh_keys、user_mfa、user_mfa_backup_codes、user_verification_tokens、user_events、oauth_clients、oauth_consents、oauth_refresh_tokens、service_token_revocations、organizations、organization_members、organization_roles、organization_role_permissions、organization_member_roles、organization_member_permissions、organization_invites、organization_invite_redemptions、organization_join_rules(新增)、organization_join_requests(新增)、user_agreement_acceptances(新增)。
4.2 GitData 只读投影
| 表 | 关键字段 | 来源 |
|---|---|---|
users | uid i64、username、lower_name、display_name、avatar_url、is_active、is_restricted、prohibit_login、bot、system、visibility、version | OIDC 回调 + GetUserPublic / BatchGetUserPublic + 对账 |
spaces | id uuid(= organization id)、lower_name、name、description、owner_id i64、avatar_url、is_private、allow_join、allow_join_level、member_must_enable_2fa、welcome_*、banner_*、nums_members、version | OrganizationService 写成功后同请求双写 + 对账 |
space_members | (space_id, user_id)、alias_name、alias_avatar_url、status、joined_at、left_at、banned_at、ban_reason、ban_expires_at、ban_allow_*、message_notifications、muted、mute_until、mobile_push、suppress_*、hide_muted_channels、last_active_at | OrganizationService 写成功后同请求双写 + 对账 |
投影表只允许 GitData 内部写入,禁止绕过 gRPC 直接改。写入口集中在
lib/space/src/sync.rs与lib/kenauth调用点。
4.3 GitData 完全自有
user_profile、user_preferences、user_notifications、user_saved_message、user_recent_mention、user_reports、user_push_subscription、user_blocks、user_followers、user_favorite_meme、user_suspensions、rooms、room_*、dir、dir_overwrite_group、dir_overwrite_user、space_warn、repos、issues、pull_request*、chat_*、ci_*、agent*、search_*。
4.4 删除的数据
user_sessions、user_access_key、user_ssh_key、user_email、user_email_change_ticket、user_email_verification_codes、user_recovery_codes、user_agreement_acceptances、user_devices、space_bans、space_groups、space_group_permissions、space_member_groups、space_invite、space_invite_groups、space_join_rule、space_join_request。
5. lib/jwt 设计
5.1 定位
lib/jwt 只验签,不签发:
- 验证 KenOAuth access token(浏览器 cookie)。
- 验证 KenOAuth service token(
app/im聊天握手)。 - 不持有任何签名私钥,不依赖
models/sea-orm/redis/kenauth。
5.2 结构
lib/jwt/
Cargo.toml
src/lib.rs
src/verifier.rs # JWKS 缓存 + EdDSA 验签
src/access.rs # AccessClaims
src/service.rs # ServiceClaims
src/extract.rs # Session(FromRequestParts<S>)
src/user.rs # SessionUser trait(仅 uid())
src/cookie.rs # session / refresh cookie
src/guard.rs # require_uid / session_guard
src/error.rs # SessionError
src/share.rs # 兼容路径
依赖:axum、serde、serde_json、jsonwebtoken、reqwest、tokio、time、tracing、config、export。
5.3 Claims
#[derive(Clone, Deserialize)]
pub struct AccessClaims {
pub iss: String,
#[serde(deserialize_with = "subject::deserialize")]
pub sub: i64,
#[serde(rename = "client_id", alias = "app_id")]
pub client_id: String,
pub aud: String,
pub jti: String,
pub iat: i64,
pub exp: i64,
pub security_version: i32,
pub sid: Option<String>,
#[serde(default, with = "scopes")]
pub scope: Option<Vec<String>>,
pub username: Option<String>,
pub token_type: String,
}
#[derive(Clone, Deserialize)]
pub struct ServiceClaims {
pub iss: String,
#[serde(deserialize_with = "subject::deserialize")]
pub sub: i64,
pub aud: String,
pub jti: String,
pub iat: i64,
pub exp: i64,
pub security_version: i32,
#[serde(default)]
pub scopes: Vec<i32>,
pub token_type: String,
}
sub 在 KenOAuth 中序列化为字符串,必须自定义反序列化;scope 支持空格分隔字符串与数组两种形态。
5.4 验签规则
| 检查 | access | service |
|---|---|---|
| 算法 | EdDSA | EdDSA |
| 签名键 | JWKS 按 kid 选取 | 同 |
iss | [oauth] issuer | 同 |
aud | [oauth] audience(默认 gitdata.ai) | chat.gitdata.ai |
client_id | 必须等于 gitdata-web | 不适用 |
token_type | "access" | "service" |
JOSE typ | at+jwt | JWT |
exp / iat | leeway_secs(默认 30) | 同 |
未知 kid | 限速刷新 JWKS(≥30s) | 同 |
| JWKS 不可用 | 有缓存用缓存;无缓存返回 Verifier | 同 |
5.5 Session 提取器
enum SessionState { Anonymous, Expired, Valid(AccessClaims) }
pub struct Session {
state: SessionState,
raw: Option<String>,
}
impl Session {
pub fn uid(&self) -> Option<i64>;
pub fn username(&self) -> Option<&str>;
pub fn session_id(&self) -> Option<&str>;
pub fn security_version(&self) -> Option<i32>;
pub fn scopes(&self) -> &[String];
pub fn token(&self) -> Option<&str>;
pub fn is_authenticated(&self) -> bool;
pub fn is_expired(&self) -> bool;
}
- 来源:cookie
gitdata_session,回退Authorization: Bearer <access token>。 - 无 cookie / 验签失败 →
Anonymous。 - 签名有效但
exp已过 →Expired。 - 提取器永不返回错误;鉴权由
require_uid/ 中间件决定。
5.6 SessionUser 与 require_uid
pub trait SessionUser {
fn uid(&self) -> Option<i64>;
}
impl SessionUser for Session { ... }
pub fn require_uid(session: &Session) -> Result<i64, SessionError>;
全仓 17 处 SessionUser 导入、204 处 require_uid 调用保持原样,仅 import 路径改为 jwt。
5.7 Cookie
| Cookie | 值 | HttpOnly | SameSite | Path | Max-Age |
|---|---|---|---|---|---|
gitdata_session | KenOAuth access token | 是 | Lax | / | token 剩余 TTL |
gitdata_refresh | KenOAuth refresh token | 是 | Strict | /api/v1/auth | 30 天 |
Secure由[oauth] cookie_secure控制;生产必须为 true。Domain由[oauth] cookie_domain控制,默认空(host-only)。
5.8 错误
pub enum SessionError {
Unauthorized, // 401 unauthenticated
Expired, // 401 token_expired
Verifier(String), // 401 invalid_token
}
export::api_error! 宏中的 ::session_layer::SessionError 改为 ::jwt::SessionError,删除 Database 分支。
5.9 兼容层
pub mod share {
pub mod session { pub use crate::require_uid; }
pub mod guard { pub use crate::guard::session_guard; }
}
迁移时只需把 412 个文件中的 session_layer 替换为 jwt。
5.10 JWKS 缓存
RwLock<CachedJwks>+tokio::sync::Mutex串行刷新。- 正常 TTL
jwks_cache_secs(3600)。 - 遇到未知
kid时立即刷新,但两次刷新间隔不小于jwks_min_refresh_secs(30)。 - 启动时尝试加载;失败只告警,首个请求再重试(开发环境容忍 KenOAuth 未启动)。
6. 认证流程
6.1 OIDC 登录
GET /api/v1/auth/login?redirect=/app
1. 校验 redirect 为站内相对路径
2. 生成 state / nonce / code_verifier,写 Redis 600s
3. 302 {issuer}/api/v1/oauth/authorize
?response_type=code
&client_id=gitdata-web
&redirect_uri={redirect_uri}
&scope=openid profile email
&state=&nonce=&code_challenge_method=S256&code_challenge=
GET /api/v1/auth/callback?code&state
1. state 校验并删除
2. POST {issuer}/api/v1/oauth/token (authorization_code + code_verifier)
3. 验 id_token:JWKS、iss、aud、exp、nonce、at_hash
4. 验 access_token:JWKS、iss、aud、client_id、typ、exp
5. sub 解析为 i64
6. Set-Cookie session + refresh
7. 302 redirect 或 /app
异常处理:
| 情况 | 响应 |
|---|---|
| state 缺失/过期 | 400 |
token 端点 invalid_grant | 400 |
| token 端点 5xx | 502 |
| id_token 验签失败 / nonce 不匹配 | 401 |
sub 非整数 | 401 |
| 用户被禁用 / 已删除 | 403 |
6.2 刷新
POST /api/v1/auth/refresh
1. 读 gitdata_refresh;缺失 → 401 unauthenticated
2. 从 access token 读 sid(签名有效即可,忽略 exp)
3. Redis SET oauth:refresh:{sid} NX EX 10
4. POST {issuer}/api/v1/oauth/token (grant_type=refresh_token)
5. 覆盖 gitdata_session / gitdata_refresh
6. 204
- invalid_grant → 清两个 cookie → 401 refresh_expired
- 拿不到锁 → 等待 ≤2s 后重试一次
前端策略:
- 401
token_expired→ 单飞调用/auth/refresh→ 重放原请求。 - 401
refresh_expired/unauthenticated→ 跳/api/v1/auth/login?silent=1。 silent=1时授权请求带prompt=none;login_required→ 清 cookie → 交互登录。
6.3 吊销复核
全局中间件 lib/auth/src/oauth/revalidate.rs:
- 跳过路径:
/healthz、/readyz、/api/v1/auth/*、静态资源、SPA fallback。 - Redis key
oauth:recheck:{sid}:{sv},TTL 60s。 - 未命中 →
AuthorityService.GetSession(user_id, version, sid)。 - 失败 → 清 cookie,返回 401
session_revoked。
6.4 登出
POST /api/v1/auth/logout
1. 读 claims.sid
2. SessionService.RevokeSession(uid, sid, "user_logout")
3. 清 gitdata_session + gitdata_refresh
4. 302 /
6.5 敏感操作 MFA
- 改口令、增删 Access Token、增删 SSH Key、管理类操作:额外调用
AuthorityService.GetSession,检查mfa_verified。 - 未通过 → 403
mfa_required。
6.6 聊天令牌
apihost GET /api/v1/auth/jwt-request?appid=chat
→ TokenService.IssueServiceToken(
audience = TOKEN_AUDIENCE_CHAT,
subject_user_id = uid,
scopes = [event, guild],
ttl_seconds = 900)
→ { jwt, expires_in_secs }
im 握手:
lib/jwt::service::verify(token, aud="chat.gitdata.ai")
→ consume_jti(claims.jti)(Redis 单次)
→ TokenService.IntrospectToken(token)
→ handle_socket(uid)
删除 lib/export/src/jwt.rs 与 auth.chat_jwt_sec。
7. KenOAuth 改造(K 系列)
约定:所有新增 RPC 仍走
auth.oauth.rpc_key(x-rpc-key),mutation 需gitdata-request-id。
K0 配置与客户端注册(0.5 人日)
- 新增
gitdata-webpublic 客户端(PKCE)。 - 新增
auth.oauth.invite_accept_url。 - 新增组织邀请邮件模板与 i18n 文案。
auth:
oauth:
public_url: "http://127.0.0.1:18081"
rpc_key: "<与 GitData oauth.rpc_key 一致,>=32 字符>"
invite_accept_url: "http://localhost:5173/spaces/invites/accept?token={token}"
clients:
- client_id: gitdata-web
name: "GitData Web"
kind: public
redirect_uris:
- "http://localhost:5173/api/v1/auth/callback"
post_logout_redirect_uris:
- "http://localhost:5173/"
scopes: [openid, profile, email, user_name, guild, event]
验收:GET /.well-known/openid-configuration 正常;未注册 client 返回 invalid_client。
K1 用户模型与 Bot 账号(2 人日)
表变更:
users新增bot bool default false、system bool default false。
proto:
message Principal {
// ...
bool bot = 14;
bool system = 15;
}
message UserPublic {
// ...
bool bot = 8;
bool system = 9;
}
message AuthorityUser {
// ...
bool bot = 19;
bool system = 20;
}
service IdentityService {
rpc EnsureBotUser(EnsureBotUserRequest) returns (EnsureBotUserResponse);
}
message EnsureBotUserRequest {
string username = 1;
string displayname = 2;
bool system = 3;
}
message EnsureBotUserResponse {
auth.common.v1.UserPublic user = 1;
bool created = 2;
}
验收:连续两次 EnsureBotUser("service-bot") 返回同一 user_id。
K2 协议接受(1 人日)
新增表 user_agreement_acceptances(user_id、key、version、accepted_at,唯一键 (user_id, key))。
service AgreementService {
rpc AcceptAgreement(AcceptAgreementRequest) returns (AcceptAgreementResponse);
}
message AcceptAgreementRequest {
int64 user_id = 1;
string key = 2;
string version = 3;
}
message AcceptAgreementResponse {
bool accepted = 1;
google.protobuf.Timestamp accepted_at = 2;
}
验收:接受后 CheckAgreementsAccepted 返回 accepted=true;重启后仍为 true。
K3 MFA 注册与恢复码(3 人日)
service MfaService {
rpc EnrollTotp(EnrollTotpRequest) returns (EnrollTotpResponse);
rpc ConfirmTotp(ConfirmTotpRequest) returns (ConfirmTotpResponse);
rpc DisableTotp(DisableTotpRequest) returns (DisableTotpResponse);
rpc RegenerateRecoveryCodes(RegenerateRecoveryCodesRequest)
returns (RegenerateRecoveryCodesResponse);
}
message EnrollTotpRequest { int64 user_id = 1; }
message EnrollTotpResponse {
string factor_id = 1;
string secret_base32 = 2;
string otpauth_url = 3;
google.protobuf.Timestamp expires_at = 4;
}
message ConfirmTotpRequest { int64 user_id = 1; string factor_id = 2; string code = 3; }
message ConfirmTotpResponse { bool enabled = 1; repeated string recovery_codes = 2; }
message DisableTotpRequest { int64 user_id = 1; string code = 2; }
message DisableTotpResponse { bool disabled = 1; }
message RegenerateRecoveryCodesRequest { int64 user_id = 1; string code = 2; }
message RegenerateRecoveryCodesResponse { repeated string recovery_codes = 1; }
要点:secret 用 auth.totp_encryption_key 加密落库;恢复码只返回一次;关闭与重生成需校验动态码并推进 security_version。
K4 组织扩展(6 人日)
K4.1 表变更
ALTER TABLE organizations ADD COLUMN join_policy text NOT NULL DEFAULT 'invite_only';
ALTER TABLE organizations ADD COLUMN allow_join boolean NOT NULL DEFAULT false;
ALTER TABLE organizations ADD COLUMN require_email_verified boolean NOT NULL DEFAULT true;
ALTER TABLE organizations ADD COLUMN require_mfa boolean NOT NULL DEFAULT false;
ALTER TABLE organizations ADD COLUMN welcome_message text;
ALTER TABLE organizations ADD COLUMN member_must_enable_2fa boolean NOT NULL DEFAULT false;
ALTER TABLE organization_roles ADD COLUMN color text;
ALTER TABLE organization_roles ADD COLUMN emoji text;
ALTER TABLE organization_roles ADD COLUMN icon_hash text;
ALTER TABLE organization_roles ADD COLUMN position bigint NOT NULL DEFAULT 0;
ALTER TABLE organization_members ADD COLUMN last_active_at timestamptz;
ALTER TABLE organization_invites ADD COLUMN email text;
join_policy:open / approval / invite_only / private。
K4.2 新增表
organization_join_rules:id、organization_id、role_id、trigger、matcher、enabled、timestamps。organization_join_requests:id、organization_id、user_id、status、note、decided_by、decided_at、timestamps。
K4.3 新增 RPC
service OrganizationService {
rpc CreateJoinRule(CreateJoinRuleRequest) returns (CreateJoinRuleResponse);
rpc UpdateJoinRule(UpdateJoinRuleRequest) returns (UpdateJoinRuleResponse);
rpc DeleteJoinRule(DeleteJoinRuleRequest) returns (DeleteJoinRuleResponse);
rpc ListJoinRules(ListJoinRulesRequest) returns (ListJoinRulesResponse);
rpc CreateJoinRequest(CreateJoinRequestRequest) returns (CreateJoinRequestResponse);
rpc ListJoinRequests(ListJoinRequestsRequest) returns (ListJoinRequestsResponse);
rpc WithdrawJoinRequest(WithdrawJoinRequestRequest) returns (WithdrawJoinRequestResponse);
rpc ApproveJoinRequest(ApproveJoinRequestRequest) returns (ApproveJoinRequestResponse);
rpc RejectJoinRequest(RejectJoinRequestRequest) returns (RejectJoinRequestResponse);
rpc EnsureBotMember(EnsureBotMemberRequest) returns (EnsureBotMemberResponse);
}
ApproveJoinRequest 与 AddMember 共用同一事务函数;EnsureBotMember 跳过 actor 权限判定。
K4.4 邮件邀请
CreateInviteRequest增string email = 8。OrganizationJoinMethod增ORGANIZATION_JOIN_METHOD_INVITE_EMAIL = 4。CreateInvite当method=INVITE_EMAIL时:生成一次性 token(只存哈希)、落库、通过内嵌EmailService发送mail_organization_invite。- 链接来自
auth.oauth.invite_accept_url。
验收:建组织 → join_policy=approval → 申请 → 批准 → 成员列表出现;邮件邀请可兑换。
K5 账号自助(4 人日)
| 能力 | 新增 |
|---|---|
| 登录后改口令 | POST /auth/password/change + ChangePassword RPC |
| 改邮箱 | POST /auth/email/change、/auth/email/change/confirm、DELETE /auth/email/change/{ticket_id} |
| 注销账号 | POST/DELETE /auth/deletion、GET /auth/deletion + 延迟清理 worker |
| Access Token 更新/轮换 | PATCH /auth/access-tokens/{id}、POST /auth/access-tokens/{id}/rotate |
| 会话列表/吊销 HTTP | GET /auth/sessions、DELETE /auth/sessions/{sid}、DELETE /auth/sessions |
要点:改口令后 security_version + 1 并吊销其他会话;改邮箱复用 VerificationPurpose::EmailChange;注销软删 + 宽限期。
8. GitData 改造(B 系列)
B0 通道与基础设施(3 人日)
新增 lib/kenauth:
lib/kenauth/
Cargo.toml
build.rs
proto/** # 从 KenOAuth 复制
src/lib.rs
src/client.rs # identity/authority/session/org/token/access_key/ssh_key
src/error.rs # ErrorDetail -> ApiError
src/convert.rs # Principal / UserPublic -> 本地 DTO
要点:
tonic_prost_build编译 proto,只生成 client。- 拦截器注入
x-rpc-key、gitdata-request-id。 - 超时:读 300ms,写 2s。
AppState增加kenauth: Option<Arc<KenAuthClient>>(kenauth不依赖export)。
基础设施:
docker-compose.dev.yml增加nats、oauth。charts/core增加oauth.yaml(Deployment/Service/ConfigMap/Secret)。Dockerfile增加 oauth 镜像阶段。scripts/dev.*、justfile增加 oauth。lib/health增加 oauth 探测。
验收:grpcurl -H 'x-rpc-key: ...' 127.0.0.1:18082 auth.v1.IdentityService/GetUserStatus 可用。
B0.5 删除管理后台(2 人日)
删除:
| 路径 | 说明 |
|---|---|
lib/admin/ | ~7.5k 行 |
lib/admin-rpc/ | ~2.5k 行 + proto |
app/adminhost/ | 73 行 |
charts/core/templates/adminhost.yaml | — |
charts/core/values.yaml 的 adminhost 段 | — |
charts/prod/values/prod.yaml 的 adminhost 段 | — |
lib/config/src/admin.rs | — |
config/default.toml 的 [admin] | — |
apihost.admin_uids | — |
lib/auth/src/share/guard.rs::admin_guard | — |
scripts/dev.ps1、scripts/dev.sh 中 adminhost | — |
justfile urls 中 adminhost | — |
Dockerfile 中 adminhost 二进制 | — |
Cargo.toml 的 admin / admin-rpc workspace 依赖 | — |
新增 [internal] service_key,把 /api/v1/internal/* 改为服务密钥守卫:
- 请求头
x-gitdata-service-key。 - 常量时间比较。
- 仅内网可达。
- 实施时审计路由集合,删除无消费者的 admin-only 端点。
验收:全仓无 adminhost / admin_rpc 引用;/api/v1/internal/* 无密钥 401。
B1 用户 ID 全面 i64(8 人日)
原则:只改用户标识,不改 space/dir/repo/issue/room/ci 等资源主键。
model 层:
| 文件 | 改动 |
|---|---|
lib/models/src/users/user.rs | uid: Uuid → i64;删除 passwd_hash、passwd_version、password_last_changed_at、totp_* |
lib/models/src/**(87 个文件) | 用户外键 Uuid → i64 |
lib/models/src/space/space_members.rs | user_id: i64 |
非 model 层:
| 文件 | 改动 |
|---|---|
lib/export/src/authz.rs | OnceLock<i64>、HashSet<i64> |
lib/export/src/space_token.rs | bot_uid: i64 |
lib/session-layer/src/user.rs | 随 B2 删除 |
lib/auth/src/share/guard.rs | 随 B2 删除 |
lib/config/src/apihost.rs | 删除 admin_uids |
lib/models/src/** | 同步索引与关系 |
前端:just openapi + bun run --cwd web gen:api;把 user_id 当字符串处理的地方改为 number。
执行方式:先改 users.uid,cargo check 按编译错误逐个修复。
验收:cargo check --workspace 全绿;openapi-check 通过。
B2 lib/jwt 与认证替换(21.5 人日)
B2.0 lib/jwt 实现(4 人日)
按 §5 实现。
B2.1 webapi 接入(1.5 人日)
let verifier = Arc::new(JwtVerifier::new(&state.config.oauth).await?);
...
.layer(Extension(verifier))
.layer(from_fn_with_state(state.clone(), auth::oauth::revalidate))
删除 session_layer::{SessionLayer, store} 与 store::connect。
B2.2 OIDC 回调 / refresh / logout(4 人日)
新增:
lib/auth/src/oauth/
mod.rs
state.rs # Redis: oauth:state:{state}
jwks.rs # 由 lib/jwt 提供
callback.rs
refresh.rs
logout.rs
revalidate.rs
B2.3 全局替换(1.5 人日)
- 412 个文件
session_layer→jwt。 - 8 个
share/guard.rs保留,session_guard改为非泛型或空泛型。 - 删除 8 个
impl SessionGuard块。 app/im的SessionGuard引用改为lib/jwt::service。lib/export/src/api_error.rs宏引用::jwt::SessionError。
B2.4 删除旧登录链路(4 人日)
删除:
lib/auth/src/handlers/*中 register/login/login-2fa/logout/email/password/2fa/agreements/access-keys/ssh-keys/jwt-request。lib/auth/src/services/{login,register,password,two_factor,email,sessions,devices,agreements,access_keys,ssh_keys,pow,jwt}.rs。lib/auth/src/pow/**、lib/auth/src/templates/**、lib/auth/src/share/{crypto,totp,rate}.rs。lib/pow-solver。lib/user/src/services/{emails,sessions}.rs及对应 handlers。
B2.5 删除两个 crate(1 人日)
- 删除
lib/session/、lib/session-layer/。 Cargo.toml移除session、session-layer。- 14 个 crate 的
Cargo.toml:session-layer→jwt。 lib/config:session.rs→ 删除;[session]→[oauth]。lib/webapi/Cargo.toml移除session。
B2.6 app/im 改造(2 人日)
GET /api/v1/auth/jwt-request改为TokenService.IssueServiceToken。app/im/src/gateway.rs改为lib/jwt::service::verify+consume_jti+IntrospectToken。- 删除
lib/export/src/jwt.rs。
B2.7 前端(2 人日)
web/src/shared/api/createApis.ts:401 拦截 + 单飞 refresh。web/src/features/auth/model/session.ts:401 跳转逻辑。- 删除登录/注册/重置/2FA 页面与
features/auth中相关逻辑。 web/src/app/routes/router.tsx去掉 6 个路由。web/src/shared/lib/pow-solver删除。
B2.8 配置 / Helm(1.5 人日)
见 §12。
验收:
- 登录 → 回调 → cookie 写入 →
/me正确 - 401 → refresh → 重放成功
- 吊销会话 60s 内 401
- 全仓无
session_layer
B3 Git 凭据与令牌(5 人日)
| 文件 | 改动 |
|---|---|
lib/git/src/share/access.rs::verify_access_key | 改为 AccessKeyService.VerifyAccessKey |
lib/git/src/ssh/handler/auth.rs | 改为 SshKeyService.LookupSshKey |
lib/git/src/share/access.rs::verify_space_bot_token | bot_uid: i64 |
lib/models/src/users/{user_access_keys,user_ssh_keys}.rs | 删除 |
app/im | 已由 B2.6 覆盖 |
scope 映射:KenOAuth repo_read/repo_write → 本地 allows(write)。
B4 Space 投影与写入穿透(15 人日)
B4.1 同步层
新增 lib/space/src/sync.rs:
upsert_space/remove_spaceupsert_member/remove_memberbump_members
映射:
| OrganizationInfo | spaces |
|---|---|
id | id |
slug | lower_name |
name | name |
description | description |
avatar_url | avatar_url |
owner_user_id | owner_id |
member_count | nums_members |
version | version |
| OrganizationMemberInfo | space_members |
|---|---|
user_id | user_id |
role / left_at / banned_at | status |
alias_* | 同名 |
ban_* | 同名 |
| 通知偏好字段 | 同名 |
last_active_at | 同名 |
B4.2 服务改造
| 文件 | 改动 |
|---|---|
lib/space/src/services/spaces.rs | 创建/更新/归档/删除 → OrganizationService |
lib/space/src/services/members.rs | → 成员 RPC + 投影 |
lib/space/src/services/bans.rs | → BanMember/UnbanMember/UpdateBan |
lib/space/src/services/groups.rs | → Role RPC |
lib/space/src/services/invites.rs | → Invite RPC |
lib/space/src/services/join_rules.rs | → K4 RPC |
lib/space/src/services/join_requests.rs | → K4 RPC |
lib/space/src/share/authz.rs | owner/system 短路 + CheckPermission(300s)+ 投影 |
lib/space/src/services/rooms.rs、dir.rs、images.rs | 保留本地 |
B4.3 删除表
space_bans、space_groups、space_group_permissions、space_member_groups、space_invite、space_invite_groups、space_join_rule、space_join_request。
B4.4 权限映射
| 角色 | key | 权限 |
|---|---|---|
| OWNER | owner | 全部(短路) |
| ADMIN | admin | view_space, read, message, manage_members, manage_invites, manage_bans, manage_channels, manage_overwrites |
| MEMBER | member | view_space, read, message |
自定义角色 = 原 space_groups;self_assignable = 原 join_group_self。
验收:建 space → 成员/角色/封禁/邀请/加入申请全链路;24 个跨域 space_members 查询正常。
B5 收尾(4 人日)
lib/webapi/src/lib.rs路由收敛;/internal服务密钥守卫。lib/export/src/authz.rs清理。lib/user、lib/auth死代码清理。openapi.json+web/src/shared/api最终生成;openapi-check通过。- Helm/compose/justfile 最终校验。
- 全链路冒烟:注册 → 登录 → 建组织 → 邀请 → 建仓库 → push → issue → chat → CI。
9. 端点处置表
| 原端点 | 处置 |
|---|---|
GET /auth/pow/challenge | 删除 |
POST /auth/register | 删除 |
POST /auth/login | 改为 GET 302 |
POST /auth/login/2fa | 删除 |
POST /auth/logout | 改为本地登出 + 302 |
POST /auth/email/verify、/resend、/reclaim | 删除 |
POST /auth/password/forgot、/reset、/change | 删除 |
GET /auth/agreements | 保留,代理 AgreementService.ListAgreements |
PUT /me/agreements/{key} | 改为代理 AcceptAgreement |
POST /auth/email/change* | 改为代理 KenOAuth |
GET /auth/2fa/status | 代理 MfaService.GetMfaStatus |
| `POST /auth/2fa/setup | enable |
GET/PUT /auth/2fa/recovery-codes | 代理 K3 RPC |
GET/POST/DELETE/PATCH /auth/access-keys* | 代理 KenOAuth HTTP |
GET/POST/DELETE /auth/ssh-keys* | 代理 KenOAuth HTTP |
GET /auth/jwt-request | 改为 TokenService.IssueServiceToken |
GET/PATCH /me | 保留;账号字段写 AuthorityService.UpdateUser |
PUT /me/username | 代理 AuthorityService.UpdateUser |
GET/PATCH /me/preferences | 保留本地 |
GET/DELETE /me/sessions* | 代理 SessionService |
GET/POST/DELETE /me/emails* | 删除(单邮箱) |
GET/PATCH/DELETE /me/devices* | 删除 |
GET/POST/DELETE /me/deletion | 改为代理 K5 |
GET /users* | 保留,读投影 |
10. 数据模型变更(GitData)
| 表 | 操作 |
|---|---|
users | uid BIGINT;删凭据列;保留 bot/system |
user_sessions | 删除 |
user_access_key | 删除 |
user_ssh_key | 删除 |
user_email*、user_email_change_ticket、user_email_verification_codes | 删除 |
user_recovery_codes | 删除 |
user_devices | 删除 |
user_agreement_acceptances | 删除 |
space_bans | 删除 |
space_groups | 删除 |
space_group_permissions | 删除 |
space_member_groups | 删除 |
space_invite、space_invite_groups | 删除 |
space_join_rule、space_join_request | 删除 |
spaces | 保留;补 join_policy、require_email_verified、require_mfa、version |
space_members | 保留;补 banned_at、ban_reason、ban_expires_at、ban_allow_*、last_active_at |
| 其余 model | 用户外键 UUID → BIGINT |
建表仍由 models::migrate::sync_schema 完成;空库无需手写迁移 SQL。
11. 前端改造
| 区域 | 改动 |
|---|---|
| 认证页面 | 删除 6 个页面与对应路由 |
features/auth | 保留会话读取与错误翻译;删除登录/注册/POW |
features/user-settings | 密码/2FA/邮箱/会话/Access Token/SSH Key 改调代理端点 |
features/space-management | group_id → role_id;封禁字段;加入规则/申请字段 |
entities/* | 用户 id 由 string 改 number |
shared/api | globals.d.ts、apiDefinitions.ts 重新生成 |
createApis.ts | 401 拦截 + 单飞 refresh |
shared/storage | setCacheOwner 参数改 number |
| i18n | 删除废弃文案,补新错误码 |
12. 配置与基础设施
12.1 GitData 配置
[oauth]
enabled = true
issuer = "http://127.0.0.1:18081"
client_id = "gitdata-web"
client_secret = ""
redirect_uri = "http://localhost:5173/api/v1/auth/callback"
token_endpoint = "http://127.0.0.1:18081/api/v1/oauth/token"
jwks_url = "http://127.0.0.1:18081/.well-known/jwks.json"
audience = "gitdata.ai"
grpc_endpoint = "http://127.0.0.1:18082"
rpc_key = ""
session_cookie = "gitdata_session"
refresh_cookie = "gitdata_refresh"
cookie_domain = ""
cookie_path = "/"
cookie_secure = false
cookie_same_site = "lax"
access_ttl_secs = 600
refresh_cookie_max_age_secs = 2592000
recheck_secs = 60
leeway_secs = 30
jwks_cache_secs = 3600
jwks_min_refresh_secs = 30
http_timeout_ms = 2000
[internal]
service_key = ""
删除:[session]、[admin]、apihost.admin_uids、auth.chat_jwt_sec。
12.2 本地开发
# docker-compose.dev.yml
nats:
image: nats:2-alpine
ports: ["127.0.0.1:4222:4222", "127.0.0.1:8222:8222"]
command: ["-js", "-m", "8222"]
oauth:
image: harbor.gitdata.me/app/kenoauth:latest
network_mode: host
volumes:
- ./config/oauth.yaml:/app/config/config.yaml:ro
- ./config/api/keys:/app/config/api/keys:ro
depends_on: [postgres, redis, nats]
config/local.toml 增 [oauth]、[internal];config/oauth.yaml 单独维护(gitignore)。
12.3 Helm
- 新增
charts/core/templates/oauth.yaml。 - 删除
adminhost.yaml与adminhostvalues。 - 删除
APP_SESSION__*、APP_SESSION__KEY、APP_ADMIN__*。 - 新增
APP_OAUTH__ISSUER、APP_OAUTH__AUDIENCE、APP_OAUTH__CLIENT_ID、APP_OAUTH__GRPC_ENDPOINT、APP_OAUTH__RPC_KEY、APP_INTERNAL__SERVICE_KEY。 RUST_LOG去掉session::session_store。
12.4 监控
lib/kenauth客户端指标:请求量、错误率、P99。- JWKS 拉取失败告警。
oauth unavailable持续 30s 告警。- 健康检查失败不清空本地 cookie。
13. 批次、依赖、工期
13.1 KenOAuth
| 批次 | 内容 | 人日 |
|---|---|---|
| K0 | 配置 + gitdata-web 客户端 | 0.5 |
| K1 | bot 字段 + EnsureBotUser | 2 |
| K2 | 协议接受 | 1 |
| K3 | MFA 注册 | 3 |
| K4 | 组织扩展 | 6 |
| K5 | 账号自助 | 4 |
| 小计 | 16.5 |
13.2 GitData
| 批次 | 内容 | 依赖 | 人日 |
|---|---|---|---|
| B0 | lib/kenauth + 基础设施 | K0 | 3 |
| B0.5 | 删除管理后台 + /internal 服务密钥 | — | 2 |
| B1 | 用户 ID 全面 i64 | — | 8 |
| B2.0 | lib/jwt | B0 | 4 |
| B2.1 | webapi 接入 | B2.0 | 1.5 |
| B2.2 | OIDC / refresh / logout | K5/B2.1 | 4 |
| B2.3 | 全局替换 + 删 SessionGuard | B2.1/B1 | 1.5 |
| B2.4 | 删除旧登录链路 | K1/K2/K3 | 4 |
| B2.5 | 删除两个 crate | B2.3/B2.4 | 1 |
| B2.6 | app/im 服务令牌 | B2.0 | 2 |
| B2.7 | 前端 | B2.2 | 2 |
| B2.8 | 配置 / Helm | B2.5 | 1.5 |
| B3 | Git 凭据 | B2 | 5 |
| B4 | Space 投影 | K4/B1/B2 | 15 |
| B5 | 收尾 | 全部 | 4 |
| 小计 | 58.5 |
13.3 总计
约 75 人日。
关键路径:
B0.5 ──► B0 ──► B1 ──► B2.0 ──► B2.1 ──► B2.2 ──► B2.3 ──► B2.4 ──► B2.5 ──► B3/B4 ──► B5
▲
K0 ──► K5 ───────────────────────────┘
K4 ─────────────────────────────────────────► B4
可并行:
- B0.5 与 K 系列完全独立,可最先做。
- B1 与 K 系列并行。
- B3 与 B4 并行。
14. 验收标准
14.1 功能
-
注册(KenOAuth 页面)→ 回跳 GitData →
/me正确 - 登录 / 登出 / 找回口令 / 改口令 / 改邮箱
- TOTP 注册、登录验证、恢复码、关闭
- 协议列表、接受、未接受时拦截
- Access Token 创建/列出/吊销/轮换;SSH Key 添加/删除
-
git cloneHTTPS + SSH、git push - 建组织、改资料、归档/恢复/删除、转移所有权
- 成员增删、角色增删改、权限授予、自助角色加入/退出
- 邀请链接、邮件邀请、定向邀请、撤销、兑换
- 加入规则、加入申请(提交/撤回/批准/拒绝)
- 封禁 / 解封 / 更新封禁
- MCP internal key 与 space token
14.2 JWT / 会话
- cookie 中是 EdDSA access token,验签通过
-
client_id != gitdata-web的令牌被拒绝 - access token 过期后 refresh 成功,原请求重放成功
- refresh 轮换后旧 token 重放被拒绝
-
并发 10 个刷新只触发一次
/api/v1/oauth/token - 吊销会话后 60s 内 401
-
im用 service token 握手成功,jti重放被拒
14.3 管理后台删除
-
adminhost进程、端口、Helm 资源、脚本条目全部消失 -
/api/v1/internal/*无服务密钥 401,有密钥放行 -
全仓无
session_layer/lib/session/admin_rpc
14.4 非功能
-
cargo check --workspace全绿 -
cargo clippy --workspace --all-targets无新增告警 -
just openapi-check通过 -
just web-lint通过 - 认证 P99 < 50ms(不含 OIDC 外部调用)
- KenOAuth 不可用时已登录用户可读,写操作 503
15. 风险与回滚
| 风险 | 等级 | 缓解 |
|---|---|---|
| i64 改造遗漏 | 高 | 编译器驱动 + 独立批次 |
| refresh 并发触发轮换撤销 | 高 | Redis 锁 + 前端单飞 + 失败重登 |
| JWKS 拉取失败 | 中 | 缓存 + 限速重试 + 启动告警 |
| 删除 admin 后管理能力缺口 | 中 | 列为后续独立项目 |
删除 lib/session 后遗漏 | 中 | 编译器 + 全仓 grep |
| 投影漂移 | 中 | 同请求双写 + 每小时对账 + 每日全量 |
| NATS 新依赖 | 中 | 部署前置检查 + 健康探针 |
| service key 泄露 | 中 | ≥32 字符、仅内网、可轮换 |
| 令牌在 cookie 中被 XSS 读取 | 中 | HttpOnly + SameSite + CSP |
回滚:
- 空库期:回滚部署 + 恢复数据库快照。
- 灰度期:
[oauth] enabled临时开关,关闭后回到旧会话校验;B5 删除。
16. 已知退化与能力缺口
| 项 | 说明 |
|---|---|
| 多邮箱 | 改为单邮箱 |
| 信任设备 | user_devices 删除 |
| 手机验证 | allow_join_level 的 phone 档位降级为邮箱验证 |
| GitData 资源管理 | 删除 adminhost 后,用户/仓库/runner/队列/CI/chat 审核/系统状态均无管理入口;KenAdmin 目前只管自身用户与 KenOAuth |
| 聊天 JWT | 改为 KenOAuth service token,TTL 900s |
| 邀请统计 | invite_sort_url_hash 随表删除 |
17. 附录
17.1 错误码映射(KenOAuth Code → GitData HTTP)
| Code | HTTP | 前端处理 |
|---|---|---|
INVALID_ARGUMENT / MALFORMED_REQUEST | 400 | 展示 message |
INVALID_CREDENTIALS / UNAUTHENTICATED / TOKEN_* | 401 | 跳登录 |
SECURITY_VERSION_STALE | 401 | 清会话 + 跳登录 |
FORBIDDEN / LOGIN_FORBIDDEN / USER_DISABLED | 403 | 展示 message |
MFA_REQUIRED | 403 | 跳二次验证 |
AGREEMENT_NOT_ACCEPTED | 403 | 跳协议页 |
NOT_FOUND / USER_NOT_FOUND / ORGANIZATION_*_NOT_FOUND | 404 | 展示 message |
CONFLICT / *_TAKEN / *_EXISTS | 409 | 展示 message |
RATE_LIMITED / EMAIL_CODE_ATTEMPTS_EXCEEDED | 429 | 展示 retry_after |
UNAVAILABLE | 503 | 提示稍后重试 |
INTERNAL / DATABASE_ERROR / CACHE_ERROR | 500 | 通用错误 |
17.2 权限键
view_space
read
message
manage_members
manage_roles
manage_invites
manage_bans
manage_channels
manage_overwrites
17.3 GitData 配置模板
见 §12.1。
17.4 KenOAuth 关键文件索引
| 功能 | 文件 |
|---|---|
| HTTP 路由 | src/auth/router.rs |
| gRPC 注册 | src/auth/grpc/mod.rs |
| 组织 | src/auth/grpc/organization.rs、src/auth/organization/** |
| 身份 | src/auth/grpc/identity.rs |
| 会话 | src/auth/grpc/session.rs、src/session/** |
| 令牌 | src/auth/grpc/token.rs、src/jwt/** |
| 协议 | src/auth/grpc/agreement.rs |
| MFA | src/auth/grpc/mfa.rs、src/auth/share/mfa.rs |
| 邮件 | src/email/** |
| 配置 | src/config/** |
| 建表 | src/auth/migrate.rs、src/db/migrate.rs |
| 前端 | view/src/** |
17.5 GitData 关键文件索引
| 功能 | 文件 |
|---|---|
| 路由聚合 | lib/webapi/src/lib.rs |
| JWT 验签 | lib/jwt/** |
| 认证 | lib/auth/src/** |
| 用户 | lib/user/src/** |
| 空间 | lib/space/src/** |
| 鉴权工具 | lib/export/src/authz.rs、lib/export/src/space_token.rs |
| Git 凭据 | lib/git/src/share/access.rs、lib/git/src/ssh/handler/auth.rs |
| 模型 | lib/models/src/** |
| 配置 | lib/config/src/** |
| 前端 | web/src/** |
| 已删除 | lib/session/**、lib/session-layer/**、lib/admin/**、lib/admin-rpc/**、app/adminhost/** |