IWish Auth开发者文档
V1 Alpha GitHub

存量账号迁移与无感切换

预绑定原账号并保持角色、业务外键和历史数据不变

存量账号迁移与无感切换

本规范只适用于 legacy-migration:已经在线运行、拥有本地用户和历史业务数据的 App。目标是替换身份提供方,同时保持原用户主键、角色、客户归属、项目负责人、创建人、审批人和审计历史不变。没有需要保留的生产账号和历史身份数据时,请使用新应用接入,不要伪造迁移计划。

1. 最终用户体验

完成预绑定后,用户在切换日只经历标准 SSO:

访问原业务 App URL
-> 跳转 IWish Auth
-> 已有 Auth 会话时直接返回;否则完成飞书或外部邮箱登录
-> App 从 Session 0.3 取得 session.user.id
-> 按 iwish_auth_user_id 命中原本地成员
-> 继续使用原角色、原业务数据和原操作记录

用户不需要重新创建业务账号,不需要重新分配角色,不需要迁移历史数据,也不需要把旧密码交给 Auth。首次切换可能需要重新登录一次;已经登录 Auth Portal 或其他 App 的用户通常只经历自动跳转。

2. 不变量

迁移必须保持:

  • App 原本地用户主键不变。
  • 所有业务外键继续引用原本地用户主键。
  • App 本地角色、permission、字段和记录范围不变。
  • 历史创建人、负责人、审批人和审计记录不变。
  • Auth 只成为身份和 SSO 来源,不接管业务授权。
  • 每个 App 的 iwish_auth_user_id 唯一关联一个本地成员。
  • production 切换前完成预绑定,不在运行期按邮箱猜测身份。

禁止:

  • 用 Auth User ID 替换全部业务表的本地用户外键。
  • 导入旧密码或密码 hash 到 IWish Auth。
  • 根据姓名、手机号、未验证邮箱或模糊文本自动合并账号。
  • 登录失败时自动创建一个没有历史数据的新本地账号。
  • 把逐用户映射、邮箱、姓名、Cookie、Session 或 Secret 提交到 Git。
  • 通过长期双登录、Auth Proxy 或可伪造 Header 兜底。

3. App 本地数据模型

推荐保留原成员表并新增稳定关联:

alter table public.app_members
  add column if not exists iwish_auth_user_id uuid;

create unique index if not exists app_members_iwish_auth_user_id_unique
  on public.app_members (iwish_auth_user_id)
  where iwish_auth_user_id is not null;

不要为 iwish_auth_user_id 建立跨数据库外键。Auth 数据库与业务 App 数据库是独立系统,关联完整性由预绑定、Session 校验和本地唯一索引保证。

迁移期间建议建立 App 私有 ledger:

create table private.auth_identity_migration_ledger (
  legacy_user_id text primary key,
  iwish_auth_user_id uuid unique,
  state text not null check (
    state in ('discovered', 'prelinked', 'conflict', 'excluded', 'failed')
  ),
  match_method text check (
    match_method in (
      'feishu_employee_id',
      'verified_work_email',
      'verified_external_email',
      'manual'
    )
  ),
  source_fingerprint text not null,
  evidence_hash text,
  reviewed_by text,
  reviewed_at timestamptz,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now()
);

Ledger 必须位于不可通过浏览器 Data API 访问的私有 schema,或保存在加密离线工件中。公开迁移计划只保存计数和证据路径,不保存逐用户 PII。

4. 身份匹配优先级

优先级方法自动预绑定条件冲突处理
1feishu_employee_id本地员工号已验证,Auth 中唯一,组织一致人工核对飞书人员档案
2verified_work_email本地企业邮箱已验证,Auth 飞书员工邮箱唯一,组织一致禁止自动选择
3verified_external_email外部账号已完成 Auth 邀请和邮箱验证,本地邮箱唯一重新邀请或人工确认
4manual管理员核对业务归属和 Auth 身份双人复核高权限账号

姓名、手机号、显示名和未验证邮箱不能成为自动匹配依据。一个本地用户匹配多个 Auth 用户、多个本地用户匹配一个 Auth 用户、共享账号、缺失身份或组织不一致都必须进入 conflict

5. 七个迁移闸门

Gate 1:Inventory

  • 记录本地用户总数、启用数、停用数、外部用户数和共享账号数。
  • 盘点旧登录、密码重置、Session、API Token 和身份 Header。
  • 盘点所有引用本地用户主键的业务表和外键。
  • 盘点本地角色、permission、字段权限、数据范围和管理员入口。

Gate 2:Identity Mapping

  • 从受控 Auth 管理流程取得组织用户目录。
  • 生成只读匹配预览,不直接修改 production。
  • 将每个存量用户分类为 prelinkedconflictexcludedpending
  • 对管理员、财务、导出和删除权限账号进行人工复核。
  • production cutover_readyconflict=0pending=0

Gate 3:Authorization Preservation

  • 保留本地用户主键和业务外键。
  • 对比迁移前后每个角色的权限集合。
  • 对比项目负责人、客户归属、创建人、审批人和审计历史计数。
  • 本地角色变化仍只在业务 App 生效。

Gate 4:Staging SSO

  • 使用独立 staging App、数据库、OAuth Client、callback 和 Secret。
  • 使用脱敏快照或覆盖边界情况的合成数据。
  • 验证飞书、外部邮箱、无映射、停用、离职、跨 App 和 App access。
  • 验证 viewer/member/manager/admin 以及记录和字段范围。
  • staging 登录必须使用在线 Auth 公共文档和 SDK,不读取 Auth 源码。

Gate 5:Rollback

  • 保存上一稳定部署、数据库备份和映射快照。
  • 演练回滚部署与恢复映射数据。
  • 回滚不能恢复长期双登录或绕过 Auth 的 Header。
  • 如果 Auth 身份不可用,App 必须 fail closed。

Gate 6:Cutover

  • 短时冻结本地成员和角色变更。
  • 重新生成 inventory 与 mapping diff。
  • 原子写入预绑定映射并校验唯一索引。
  • 部署新登录版本。
  • 关闭旧登录、旧密码重置和旧 Session 接受路径。
  • 撤销旧人员 Session 和旧机器凭证。
  • 立即验证管理员、普通成员、外部用户和关键 API。

Gate 7:Observation

  • 观察至少 24 小时,推荐 168 小时。
  • 监控 SSO callback、401、403、无映射、冲突和本地授权拒绝率。
  • 将用户问题区分为身份失败、App access 失败、本地成员失败和本地权限失败。
  • 观察期通过后才删除旧密码 hash、旧 Session 表和旧登录代码。

6. 无感切换如何实现

无感来自切换前预绑定,不来自运行期邮箱猜测:

  1. 切换前把 iwish_auth_user_id 写入原本地成员。
  2. 原项目、客户和审计表继续引用本地成员 ID。
  3. 切换后 Session 0.3 的 user.id 直接命中原成员。
  4. App 使用原成员继续执行原本地授权。

未预绑定用户必须显示“账号尚未完成迁移,请联系管理员”,不能创建空账号。切换后新增用户可以走 App 明确设计的新成员 onboarding,但该流程与存量迁移分开。

7. 内部员工和外部账号

内部员工:

  • 使用飞书 SSO。
  • Auth employee.employmentStatus 必须为 active
  • 离职或停用优先阻断,即使 App 本地角色仍存在。
  • 历史业务记录不删除。

外部账号:

  • 先通过 Auth 邀请并完成邮箱验证。
  • 再预绑定到 App 原外部成员。
  • 旧 App 密码不迁移;首次使用 Auth 时由 Auth 完成激活或密码设置。
  • 不要求外部客户使用飞书。

共享账号必须在迁移前拆分。机器任务必须迁移到 Service Account/Service Token,不能绑定人员 SSO。

8. 迁移计划 1.0

每个 App 必须提交一个无 PII 的 auth-migration-plan.json

{
  "$schema": "https://auth-docs-staging.iwishapp.cn/account-migration.schema.json",
  "schemaVersion": "1.0",
  "planVersion": "2026.07.24-1",
  "environment": "staging",
  "appKey": "legacy_crm",
  "strategy": "direct_cutover_prelinked",
  "status": "mapped",
  "identityLink": {
    "localUserIdColumn": "id",
    "authUserIdColumn": "iwish_auth_user_id",
    "preserveLocalUserId": true,
    "localAuthUserIdUnique": true,
    "runtimeEmailAutolink": false,
    "allowedMatchMethods": ["verified_work_email", "manual"]
  },
  "authorization": {
    "owner": "application",
    "preserveLocalRoles": true,
    "preserveBusinessForeignKeys": true
  },
  "inventory": {
    "total": 12,
    "prelinked": 10,
    "conflicts": 0,
    "excluded": 2,
    "pending": 0
  },
  "cutover": {
    "mode": "direct",
    "oldLogin": "disable_at_cutover",
    "oldSessions": "revoke_at_cutover",
    "passwordHashes": "retain_encrypted_until_observation_end",
    "rollback": "previous_deploy_and_mapping_snapshot",
    "observationHours": 168
  },
  "dataHandling": {
    "mappingLedgerLocation": "app_private_database",
    "piiInGit": false,
    "secretsInGit": false
  },
  "gates": [
    { "id": "inventory", "status": "passed", "evidence": ["evidence/inventory.json"] },
    { "id": "identity_mapping", "status": "passed", "evidence": ["evidence/mapping-summary.json"] },
    { "id": "authorization_preservation", "status": "pending", "evidence": [] },
    { "id": "staging_sso", "status": "pending", "evidence": [] },
    { "id": "rollback", "status": "pending", "evidence": [] },
    { "id": "cutover", "status": "pending", "evidence": [] },
    { "id": "observation", "status": "pending", "evidence": [] }
  ]
}

校验命令:

iwish-auth migration validate ./auth-migration-plan.json --environment staging

Schema 只能保证格式和跨字段闸门,不替代人工身份复核、数据库备份和真实 E2E。

9. 最低验收矩阵

  • 原本地用户主键和所有关键业务外键前后完全一致。
  • 已预绑定内部员工通过 SSO 返回原账号。
  • 已预绑定外部用户通过邮箱登录返回原账号。
  • 无映射、重复映射和冲突账号 fail closed。
  • App access 撤销、Auth 停用和飞书离职立即阻断。
  • 本地角色和数据范围迁移前后一致。
  • 旧 Session 在切换后不可继续使用。
  • 旧密码登录和密码重置入口不可访问。
  • 回滚可以恢复上一部署和映射快照。
  • 逐用户映射、Secret、Session 和 Cookie 不进入 Git、构建产物或日志。

10. AI Agent 执行规则

AI 必须先生成 inventory 和只读计划,再修改认证代码。AI 不得根据猜测生成真实 Auth User ID,不得把 synthetic ID 写入 production seed,不得要求用户在聊天中提供密码、Cookie、Session 或 Client Secret。

如果目标 App 存在重复邮箱、共享账号、缺少稳定身份、未知权限分支或无法解释的业务外键,AI 必须把账号标记为冲突并停止 production 切换,不能用“先上线再修复”绕过。