CRM 后端与 MySQL 接入 IAM 对接文档
CRM 后端与 MySQL 接入 IAM 对接文档
Section titled “CRM 后端与 MySQL 接入 IAM 对接文档”1. 文档目的
Section titled “1. 文档目的”本文交付给 CRM 后端同事,用于把旧版 CRM Laravel 后端和 CRM MySQL 数据库接入当前 IAM,实现企业统一身份登录。
本次涉及两个独立系统,必须先明确边界:
| 系统 | 技术栈 | 负责人 | 本次职责 |
|---|---|---|---|
| IAM | Better Auth 1.7.1、Hono、PostgreSQL | IAM 负责人 | 创建和配置 CRM OAuth/OIDC Client,提供 issuer、Client ID、Client Secret 和 Claims;必要时修改 IAM 数据库 |
| CRM API | Laravel 9、Sanctum、Spatie Permission | CRM 后端同事 | 实现 OIDC 登录、Token 校验、用户绑定、CRM Token 签发、Back-Channel Logout |
| CRM 数据库 | MySQL | CRM 后端同事 | 执行 Laravel migration,修改 users 和 personal_access_tokens |
| CRM Web | Vue 3 | CRM 前端负责人 | 发起整页登录跳转、接收一次性 ticket、换取并保存 CRM Sanctum Token |
本文不会要求 CRM 后端同事修改 IAM PostgreSQL,也不会要求 IAM 负责人直接修改 CRM MySQL。
2. 当前基线与目标结论
Section titled “2. 当前基线与目标结论”2.1 代码基线
Section titled “2.1 代码基线”| 项目 | 基线 |
|---|---|
| IAM commit | 7fa227324f0fac6af12fbad0172c1453f29aaf22 |
| CRM API 原仓库 commit | 765c42049bac02f1bd3d17ebbf590304a79f69e6 |
| CRM API | PHP ^8.0.2、Laravel ^9.19、Sanctum ^3.0 |
| IAM | Better Auth 1.7.1,OAuth 2.1/OIDC Provider |
| IAM JWT 算法 | RS256,RSA 2048 |
| CRM 数据库 | MySQL |
CRM API 工作区中已有一套目标实现作为参考,主要文件见第 10 节。后端同事应基于自己的后端分支实现、审查并执行 migration,不能把本地未提交文件直接当作已上线结果。
2.2 方案结论
Section titled “2.2 方案结论”采用服务端 BFF 模式:
- CRM Web 不保存 OIDC Client Secret。
- CRM Web 不直接用 Authorization Code 交换 Token。
- CRM API 使用 Authorization Code + PKCE S256 与 IAM 通信。
- CRM API 必须验证 ID Token 签名及 OIDC Claims。
- IAM 负责证明用户身份和 CRM 应用准入。
- CRM 继续使用自己的
users、Sanctum Token、Spatie 角色、团队和业务权限。 - IAM Access Token 不能作为 CRM 业务接口的 Bearer Token。
- IAM
app_role当前只用于日志或后续扩展,不能覆盖 CRM 本地角色。 - 默认不自动创建 CRM 用户;用户必须先存在于 CRM。
sequenceDiagram
autonumber
participant U as 浏览器
participant W as CRM Web
participant C as CRM API
participant I as IAM
participant M as CRM MySQL
U->>W: 点击统一身份登录
W->>C: GET /api/auth/iam/start?return_to=/...
C->>C: 生成 state、nonce、PKCE 并写共享缓存
C-->>U: 302 IAM authorize
U->>I: 企业微信登录并授权
I-->>C: 302 callback?code=&state=
C->>I: 服务端用 code + verifier 换 Token
C->>I: 拉取 JWKS 并验证 ID Token
C->>I: 使用 Access Token 请求 UserInfo
C->>M: 匹配并绑定 CRM 用户
C-->>U: 302 CRM Web /iam/callback?ticket=...
W->>C: POST /api/auth/iam/exchange
C->>M: 创建带 IAM sid 的 Sanctum Token
C-->>W: 返回 CRM Token 和本地角色
3. IAM 负责人需要提供的内容
Section titled “3. IAM 负责人需要提供的内容”本节由 IAM 负责人完成,CRM 后端同事只负责提供 CRM API/Web 的实际地址并参与核对。
3.1 创建 CRM OIDC Client
Section titled “3.1 创建 CRM OIDC Client”建议通过 IAM 管理端创建,不建议直接手写 IAM 数据库记录。配置如下:
| 配置项 | 值 |
|---|---|
| Client Name | CRM |
| Client 类型 | 机密客户端 |
| Redirect URI | https://<crm-api-domain>/api/auth/iam/callback |
| Token Endpoint Auth Method | client_secret_basic |
| Grant Types | authorization_code |
| Response Types | code |
| Scope | openid profile email |
| Require PKCE | true,只接受 S256 |
| Back-Channel Logout URI | https://<crm-api-domain>/api/auth/iam/backchannel-logout |
| Back-Channel Logout Session Required | true |
| Skip Consent | 按产品要求;内部系统可评估开启 |
Back-Channel Logout URI 在生产必须是外部可访问的 HTTPS 地址,不能带凭据和 fragment。本地开发无法直接接收 IAM 的服务器间回调时,可先验证登录主流程,再在可公网访问的测试环境验收统一下线。
Redirect URI 必须逐字符一致。协议、域名、端口、路径或末尾 / 任一不同都会导致授权失败。
3.2 交付给 CRM 后端的参数
Section titled “3.2 交付给 CRM 后端的参数”IAM 负责人通过安全渠道提供:
IAM_OIDC_ISSUER=https://iam.cq-i.cn/api/authIAM_OIDC_CLIENT_ID=<crm-client-id>IAM_OIDC_CLIENT_SECRET=<crm-client-secret>Client Secret 只允许进入 CRM API 的 Secret 管理系统,不得发给 CRM Web,不得出现在仓库、群聊截图、日志或监控事件中。
3.3 IAM Claims 契约
Section titled “3.3 IAM Claims 契约”CRM 需要使用以下 Claims:
| Claim | 用途 |
|---|---|
sub |
IAM 稳定用户标识,绑定后作为 CRM 的首要匹配键 |
sid |
IAM 会话标识,用于 Back-Channel Logout 精确撤销 CRM Token |
email |
首次绑定 CRM 用户的候选邮箱 |
email_verified |
只有严格布尔值 true 才允许邮箱首次绑定 |
wecom_user_id |
优先绑定已有 CRM 企业微信用户 |
app_role |
当前仅记录,不覆盖 CRM Spatie 角色 |
IAM 当前使用 RS256 签发 ID Token 和 Logout Token。CRM 可保留 EdDSA/Ed25519 验签作为旧密钥兼容能力,但不得把 EdDSA 当作当前主算法。
4. CRM 后端代码改造
Section titled “4. CRM 后端代码改造”4.1 新增四个公开认证路由
Section titled “4.1 新增四个公开认证路由”这些路由不能放在 auth:sanctum 中间件后面:
Route::prefix('auth/iam')->group(function () { Route::get('start', [IamAuthController::class, 'start']); Route::get('callback', [IamAuthController::class, 'callback']); Route::post('exchange', [IamAuthController::class, 'exchange']); Route::post('backchannel-logout', [IamAuthController::class, 'backchannelLogout']);});| 方法与路径 | 用途 |
|---|---|
GET /api/auth/iam/start |
创建 state、nonce 和 PKCE,跳转 IAM |
GET /api/auth/iam/callback |
IAM 回调;交换 Token、校验身份、绑定用户、签发一次性 ticket |
POST /api/auth/iam/exchange |
一次性 ticket 换 CRM Sanctum Token |
POST /api/auth/iam/backchannel-logout |
验证 IAM Logout Token,按 sid 撤销 CRM Token |
4.2 start 行为
Section titled “4.2 start 行为”请求:
GET /api/auth/iam/start?return_to=/dashboard后端必须:
- 检查 IAM 配置完整。
- 校验
return_to只能是 CRM 站内路径:以/开头、不能以//开头、不能包含 scheme、反斜杠或用户信息形式。 - 使用加密安全随机数生成
state、nonce、code_verifier。 - 计算
code_challenge = BASE64URL(SHA256(code_verifier))。 - 以 state 为键,将 verifier、nonce、return_to 写入 Laravel Cache,默认 300 秒。
- 整页 302 跳转到 IAM Authorization Endpoint。
Authorization 请求必须包含:
client_idredirect_uriresponse_type=codescope=openid profile emailstatenoncecode_challengecode_challenge_method=S2564.3 callback 行为
Section titled “4.3 callback 行为”后端必须:
- 拒绝或安全处理 IAM 返回的
error。 - 校验
code和state存在。 - 使用
Cache::pull一次性消费 state 上下文,拒绝过期或重放。 - 在 CRM API 服务端调用 Token Endpoint,使用
client_secret_basic和原始code_verifier。 - 要求 Token 响应同时包含
id_token和access_token。 - 通过 IAM Discovery/JWKS 验证 ID Token。
- 要求 ID Token 包含非空
sid。 - 使用 Access Token 调用 UserInfo。
- 按第 6 节规则匹配并绑定 CRM 用户。
- 再次校验 CRM 本地
status为启用状态。 - 创建 32 字节随机、默认 60 秒有效、仅可使用一次的 ticket。
- 302 跳转到 CRM Web 的
/iam/callback?ticket=...。
URL 中只能携带随机 ticket,不能携带 IAM Access Token、ID Token、CRM Token 或用户资料。
4.4 exchange 行为
Section titled “4.4 exchange 行为”请求:
POST /api/auth/iam/exchangeContent-Type: application/json
{"ticket":"<one-time-ticket>"}后端必须:
- 使用
Cache::pull一次性消费 ticket。 - 区分“已使用”和“不存在/已过期”,两者都拒绝登录。
- 再次查询 CRM 用户并检查
status。 - 复用原 CRM 登录响应结构和本地角色逻辑。
- 创建 CRM Sanctum Token,并把 ticket 中的 IAM
sid写入personal_access_tokens.iam_session_id。 - 只把 CRM Sanctum Token 返回给前端。
成功响应应与旧 /api/auth/login 的前端消费方式兼容,至少包含:
{ "code": 200, "data": { "access_token": "<crm-sanctum-token>", "token_type": "Bearer", "user": { "id": 1, "name": "张三", "email": "zhangsan@example.com" }, "role": ["sales"], "team_leader": 0 }}4.5 Back-Channel Logout
Section titled “4.5 Back-Channel Logout”IAM 注销会向 CRM API POST:
POST /api/auth/iam/backchannel-logoutContent-Type: application/x-www-form-urlencoded
logout_token=<signed-jwt>CRM 必须验证:
- Header
typ === logout+jwt; - 签名和
kid; iss、aud、iat、exp;- 非空
jti、sid、sub; events包含http://schemas.openid.net/event/backchannel-logout;- 不能包含
nonce。
验证通过后:
- 以
jti建立短期防重放缓存。 - 删除
personal_access_tokens.iam_session_id = sid的 CRM Token。 - 重复 Logout Token 返回成功但不重复处理。
- 日志只记录必要的 sid/sub 和撤销数量,不记录完整 Logout Token。
4.6 ID Token 验证要求
Section titled “4.6 ID Token 验证要求”不能只 Base64 解码 JWT Payload。必须完成:
| 项目 | 规则 |
|---|---|
| 算法 | 当前必须支持 RS256;如保留 EdDSA,只能作为明确白名单兼容 |
kid |
必须在 IAM JWKS 中命中;未命中时清理 JWKS 缓存并允许后续重新拉取 |
iss |
严格等于 IAM_OIDC_ISSUER |
aud |
字符串或数组中必须包含 CRM Client ID |
azp |
存在时必须等于 CRM Client ID |
exp |
必须存在且未过期,可允许最多 60 秒时钟偏差 |
iat |
必须存在,不能显著晚于当前时间 |
nonce |
必须严格等于 start 阶段缓存值 |
sub |
必须是非空字符串 |
sid |
当前 CRM 方案要求存在,用于统一下线 |
必须拒绝 none、HS256 和其他未配置算法,不能根据 Token Header 动态放宽白名单。
4.7 运行依赖和缓存
Section titled “4.7 运行依赖和缓存”- 保留
ext-sodium,用于兼容旧 EdDSA/Ed25519 JWK;当前 RS256 使用 OpenSSL。 - Discovery 和 JWKS 可分别缓存 3600 秒。
- state、PKCE、ticket 和 logout
jti必须写共享缓存。 - CRM API 多实例生产环境必须使用 Redis 等共享 Cache Driver,不能使用每实例独立 file cache。
- 所有实例必须进行时间同步。
5. CRM MySQL 数据库改造
Section titled “5. CRM MySQL 数据库改造”CRM 数据库由 CRM 后端同事负责修改。IAM 负责人不执行以下 SQL 或 migration。
5.1 users 表新增字段
Section titled “5.1 users 表新增字段”| 字段 | Laravel 定义 | 约束 | 用途 |
|---|---|---|---|
iam_subject |
string nullable |
unique | IAM sub,绑定后的稳定身份主键 |
iam_linked_at |
timestamp nullable |
首次绑定时间 | |
iam_last_login_at |
timestamp nullable |
最近一次 IAM 登录时间 |
目标 migration:
Schema::table('users', function (Blueprint $table) { $table->string('iam_subject')->nullable()->unique()->after('wechat_work_userid'); $table->timestamp('iam_linked_at')->nullable()->after('iam_subject'); $table->timestamp('iam_last_login_at')->nullable()->after('iam_linked_at');});User Model 同步修改:
$fillable增加iam_subject、iam_linked_at、iam_last_login_at;$casts将两个时间字段设为datetime。
5.2 personal_access_tokens 新增字段
Section titled “5.2 personal_access_tokens 新增字段”| 字段 | Laravel 定义 | 约束 | 用途 |
|---|---|---|---|
iam_session_id |
string(191) nullable |
index | 保存 IAM ID Token 的 sid,用于精确撤销 CRM Token |
目标 migration:
Schema::table('personal_access_tokens', function (Blueprint $table) { $table->string('iam_session_id', 191)->nullable()->index();});不要给 iam_session_id 加 unique:同一 IAM Session 可以对应多个 CRM Token。
5.3 迁移前数据检查
Section titled “5.3 迁移前数据检查”新增字段本身不需要批量回填,但必须先检查旧 CRM 用户数据是否会导致首次绑定歧义:
-- 企业微信 UserID 重复;必须先人工确认SELECT wechat_work_userid, COUNT(*) AS cntFROM usersWHERE wechat_work_userid IS NOT NULL AND wechat_work_userid <> '' AND deleted_at IS NULLGROUP BY wechat_work_useridHAVING COUNT(*) > 1;
-- 邮箱忽略大小写后重复;开启邮箱首次绑定前必须处理SELECT LOWER(email) AS normalized_email, COUNT(*) AS cntFROM usersWHERE email IS NOT NULL AND email <> '' AND deleted_at IS NULLGROUP BY LOWER(email)HAVING COUNT(*) > 1;
-- 用户状态分布,确认旧数据中的启用值SELECT status, COUNT(*) AS cntFROM usersWHERE deleted_at IS NULLGROUP BY status;如果存在重复,不能依赖 Eloquent first() 随机绑定,必须在开放 IAM 登录前清理或人工确定归属。
5.4 执行 migration
Section titled “5.4 执行 migration”后端同事先备份 CRM MySQL,再执行:
php artisan migrate --pretendphp artisan migrate执行后检查:
SHOW COLUMNS FROM users LIKE 'iam_%';SHOW INDEX FROM users WHERE Column_name = 'iam_subject';SHOW COLUMNS FROM personal_access_tokens LIKE 'iam_session_id';SHOW INDEX FROM personal_access_tokens WHERE Column_name = 'iam_session_id';验收条件:
users.iam_subject可空且有唯一索引;- 两个 IAM 时间字段可空;
personal_access_tokens.iam_session_id可空且有普通索引;- 旧用户、旧 Sanctum Token、角色、团队和业务数据未被删除或重写。
6. CRM 用户匹配与绑定规则
Section titled “6. CRM 用户匹配与绑定规则”后端按以下顺序匹配,命中后停止:
users.iam_subject === sub;users.wechat_work_userid === wecom_user_id,且iam_subject IS NULL;- 仅迁移期
IAM_ALLOW_EMAIL_LINK=true时,使用 IAM 已验证邮箱匹配iam_subject IS NULL的 CRM 用户。
邮箱绑定必须同时满足:
email_verified或兼容字段emailVerified严格为布尔值true;- 邮箱格式合法;
- 不是
@wecom.invalid或@wecom.com占位邮箱; - CRM 邮箱忽略大小写后完全一致;
- CRM 用户尚未绑定其他
iam_subject。
首次匹配成功后写入:
iam_subject = IAM subiam_linked_at = 当前时间iam_last_login_at = 当前时间后续登录只按 iam_subject 匹配;邮箱或企业微信资料变化不应把账号漂移到另一个 CRM 用户。
当前范围明确不做:
- 不自动创建 CRM 用户;
- 不把
app_role写入 Spatie 角色; - 不自动分配团队、客户、订单或其他业务权限;
- 不允许未验证邮箱或占位邮箱绑定;
- 不允许一个 CRM 用户重新绑定另一个
sub; - 不删除旧账号密码登录和旧企业微信登录,是否下线由后续切换计划决定。
全员完成首次绑定并核对无误后,建议将 IAM_ALLOW_EMAIL_LINK 改为 false,只保留 iam_subject 和企业微信 UserID 的确定性匹配。
7. CRM API 环境变量
Section titled “7. CRM API 环境变量”CRM 后端需要配置:
IAM_OIDC_ISSUER=https://iam.cq-i.cn/api/authIAM_OIDC_CLIENT_ID=<由 IAM 提供>IAM_OIDC_CLIENT_SECRET=<由 IAM 安全提供>
# 必须与 IAM Client 登记值完全一致IAM_OIDC_REDIRECT_URI=https://<crm-api-domain>/api/auth/iam/callback
# CRM Web 公开回调页和登录页IAM_FRONTEND_CALLBACK=https://<crm-web-domain>/iam/callbackIAM_POST_LOGOUT_REDIRECT_URI=https://<crm-web-domain>/login
IAM_OIDC_SCOPES="openid profile email"IAM_OIDC_TOKEN_ENDPOINT_AUTH_METHOD=client_secret_basicIAM_OIDC_STATE_TTL=300IAM_OIDC_TICKET_TTL=60IAM_ALLOW_EMAIL_LINK=trueIAM_OIDC_HTTP_TIMEOUT=15IAM_OIDC_DISCOVERY_CACHE_TTL=3600IAM_OIDC_JWKS_CACHE_TTL=3600
# CRM 本地 Sanctum Token 过期时间,单位为分钟SANCTUM_TOKEN_EXPIRATION=720修改环境变量后执行:
php artisan optimize:clear生产环境通常不需要手工配置 authorize/token/userinfo/JWKS 地址,优先从 Discovery 获取。仅联调或故障隔离时使用显式 endpoint override。
8. CRM Web 与后端的接口约定
Section titled “8. CRM Web 与后端的接口约定”本节用于后端联调,不要求 CRM 后端同事修改 Vue 代码。
CRM Web 发起登录必须使用整页跳转,不能用 Axios 请求 start 后期待自动完成跨站 302:
const url = new URL(`${CRM_API_URL}/auth/iam/start`)url.searchParams.set('return_to', '/dashboard')window.location.assign(url.toString())CRM Web /iam/callback 页面应:
- 读取
ticket; - 立即使用
history.replaceState清除地址栏中的 ticket; - POST
/api/auth/iam/exchange; - 按旧账号密码登录方式保存 CRM Sanctum Token;
- 恢复登录前的安全站内路径;
- 展示
error或iam_error,但不记录 ticket。
ticket 不能写入 localStorage、埋点、日志或错误上报。CRM Web 当前仓库尚未实现 IAM 回调页面和 IAM API 封装,因此后端上线前必须与前端负责人确认联调版本。
9. 发布与联调顺序
Section titled “9. 发布与联调顺序”- CRM 后端同事备份 CRM MySQL,执行第 5.3 节数据检查。
- CRM 后端部署两条 migration。
- CRM 后端部署 OIDC 代码和共享缓存配置,但暂不向全部用户开放入口。
- IAM 负责人创建 CRM Client,配置精确 Redirect URI 和 Back-Channel Logout URI。
- IAM 负责人通过安全渠道交付 Client ID/Secret。
- CRM 后端配置环境变量并执行
php artisan optimize:clear。 - CRM 前端部署登录入口和
/iam/callback。 - 使用一个已存在、启用且角色正确的 CRM 用户进行首次绑定。
- 验收登录、重复 ticket、停用用户、错误 state/nonce 和统一下线。
- 小范围用户验证通过后再逐步开放。
10. CRM 后端目标文件清单
Section titled “10. CRM 后端目标文件清单”当前本地参考实现涉及:
| 文件 | 后端同事需要完成的内容 |
|---|---|
app/Http/Controllers/IamAuthController.php |
start、callback、exchange、backchannel logout |
app/Repositories/IamOidcRepository.php |
Discovery、PKCE、Token、UserInfo、JWKS、ID/Logout Token 验证 |
app/Services/IamUserMapper.php |
IAM Claims 到 CRM 用户的匹配与首次绑定 |
app/Models/User.php |
新增 IAM 字段 fillable/casts |
routes/api.php |
注册四个公开 IAM 路由 |
config/services.php |
IAM OIDC 配置 |
config/sanctum.php |
CRM Token 过期时间 |
.env.example |
IAM 配置项说明,不包含真实 Secret |
composer.json |
声明 ext-sodium 兼容依赖 |
database/migrations/2026_08_26_000001_add_iam_fields_to_users_table.php |
修改 users |
database/migrations/2026_08_28_000002_add_iam_session_id_to_personal_access_tokens_table.php |
修改 personal_access_tokens |
11. 测试和验收
Section titled “11. 测试和验收”11.1 后端自动化测试
Section titled “11.1 后端自动化测试”至少执行:
php artisan test \ tests/Feature/IamAuthFeatureTest.php \ tests/Unit/IamAuthTokenResponseTest.php \ tests/Unit/IamOidcIdTokenTest.php \ tests/Unit/IamOidcPkceTest.php \ tests/Unit/IamReturnToValidationTest.php \ tests/Unit/IamUserMapperTest.php必须覆盖:
- PKCE S256、state、nonce 和 Authorization URL;
- ID Token RS256 验签及错误 iss/aud/azp/exp/iat/nonce;
- Logout Token typ/events/jti/sid/sub 和重放防护;
- ticket 过期和重复消费;
iam_subject、企微 UserID、已验证邮箱三种匹配路径;- 占位邮箱、未验证邮箱、禁用用户和不存在用户;
return_toOpen Redirect 防护;- CRM Token 正确写入
iam_session_id; - Back-Channel Logout 只撤销对应 sid 的 CRM Token。
11.2 联调检查
Section titled “11.2 联调检查”- IAM Discovery 的 issuer 与
IAM_OIDC_ISSUER完全一致。 - IAM Client Redirect URI 与 CRM 配置逐字符一致。
- CRM Client 为机密客户端、
client_secret_basic、Authorization Code + PKCE。 - CRM API 能访问 IAM Discovery、JWKS、Token 和 UserInfo。
- CRM Web 不包含 Client Secret。
- 首次登录后 MySQL 正确写入
users.iam_subject和时间字段。 - 再次登录只按
iam_subject成功,不依赖邮箱继续一致。 - CRM 返回的是本地 Sanctum Token,本地角色和团队权限未改变。
- 同一 ticket 第二次提交被拒绝。
- CRM 停用用户不能通过 IAM 登录绕过本地状态。
- IAM 注销后,对应
iam_session_id的 CRM Token 被删除,其他会话不受影响。 - 日志中没有 Client Secret、Authorization Code、ID Token、Access Token、ticket 或完整 Claims。
12. 上线后的安全收口
Section titled “12. 上线后的安全收口”- 全员绑定完成后设置
IAM_ALLOW_EMAIL_LINK=false。 - 定期清理过期 Sanctum Token;不能只依赖请求时的 expiration 判断。
- 监控 OIDC callback 失败率、state/ticket 过期、JWKS 拉取失败和用户绑定失败。
- Client Secret 泄露时,由 IAM 负责人重置 Secret,CRM 后端同步更新并清理配置缓存。
- 用户解绑或换绑必须走管理员审批和审计流程,不允许直接通过登录自动覆盖
iam_subject。 - 旧账号密码和旧企微登录的下线应单独制定灰度计划,本次接入不自动删除旧能力。
13. 双方最终交付物
Section titled “13. 双方最终交付物”IAM 负责人
Section titled “IAM 负责人”- CRM Client ID 和通过安全渠道交付的 Client Secret;
- issuer、Redirect URI、Back-Channel Logout URI 配置截图或脱敏记录;
- 测试用户在 IAM 的
sub、wecom_user_id和邮箱验证状态核对结果。
CRM 后端同事
Section titled “CRM 后端同事”- 实际后端 commit/分支;
- 两条 Laravel migration 及执行结果;
- 第 5.3 节重复数据检查结果;
- 自动化测试结果;
- 登录和 Back-Channel Logout 联调记录;
- 未完成项、兼容项和旧登录下线计划。
14. 参考
Section titled “14. 参考”- Better Auth OAuth 2.1 Provider
- IAM OAuth Provider 项目文档
- IAM 当前实现:
packages/auth/src/plugins.ts、packages/auth/src/jwt-plugin-config.ts - CRM 当前参考实现:
crm-api/app/Http/Controllers/IamAuthController.php、crm-api/app/Repositories/IamOidcRepository.php