# 017 - Apple / Google / LINE 三方登录

## 背景

C 端 app 现仅支持「手机号 + 短信验证码」登录（`/infouser/user/lodeing`，登录即注册）。本特性新增 Apple / Google / LINE 三方登录，降低台湾市场获客与登录摩擦。鉴权沿用现有自写 `JwtUtil`（HS256 JWT + Redis 会话），不引入新鉴权体系。

## 锁定的关键决策（业务确认）

1. **手机号始终是主键**：三方账号只是绑在「某个手机号账号」上的快捷入口，不在 InfoUser 上落三方 ID（独立表 `info_user_oauth` 记录绑定关系）。
2. **首次使用任一三方登录强制手机验证**：拿到手机号后查 `info_user`——已注册则**关联**到该老账号（写一条 oauth 绑定），未注册则**新建**账号后再绑定。**不以邮箱做关联**。
3. **不使用 Apple 邮箱信息**：Apple 只取 identityToken 的 `sub`（稳定用户ID），忽略 email。
4. **记录登录渠道 loginType**：因一个用户可绑多家三方，loginType 不落用户表，而写入**本次会话的 JWT claim**（`provider`）。

## User Story

### US1 - 三方登录入口 (P0)
客户在登录页选 Apple/Google/LINE，客户端拿到 provider 凭证后调后端校验。

### US2 - 首次登录手机验证 + 绑定 (P0)
首次用某三方账号登录时，强制输手机号 + 短信验证码：手机号已注册→关联该账号；未注册→新建账号；随后建立 oauth 绑定。

### US3 - 已绑定快捷登录 (P0)
已绑过该三方的账号再次登录，校验凭证通过即直接签发 token，免手机验证。

## Functional Requirements

- **FR-001**: 提供 `POST /infouser/user/oauthLogin {provider, credential, ...}`，后端校验 provider 凭证换取稳定 providerUid。
- **FR-002**: 凭证校验三选一：Apple=验 ES256 identityToken 取 sub；Google=tokeninfo HTTP 验真取 sub 并校 audience；LINE=前端传授权 code，后端用 code+clientSecret+clientId 向 `oauth2/v2.1/token` 换 access_token，再调 v2/profile 取 userId（Authorization Code 流程，不直接收前端 accessToken）。
- **FR-003**: 按 (provider, providerUid) 查 `info_user_oauth`：命中→校验用户 status/del_flag 正常后直接签发 token（claim `provider`）返回；未命中→缓存 {provider,providerUid} 到 Redis(短TTL)，返回 `needPhone` + tempKey。
- **FR-004**: 提供 `POST /infouser/user/oauthBindPhone {tempKey, phone, code, ...}`：取回缓存的 providerUid + 验短信码（复用 lodeing 逻辑，含万能码 8888）→ `getuser(phone)`：已注册→关联；未注册→`createUser` 新建；随后 insert `info_user_oauth(user_id, provider, provider_uid)`。
- **FR-005**: 新建用户昵称统一用手机号（与现有 createUser 一致），avatar 留空，不用 provider 的昵称/头像。
- **FR-006**: token claim 增加 `provider` 字段（apple/google/line；手机号登录为 phone），供登录渠道统计或「未绑手机限制」类约束使用。
- **FR-007**: 登录端点 `@Anonymous` 放行；受保护接口继续走 `@Auth`，零额外接入。
- **FR-008**: 凭证校验失败 / tempKey 过期 / 短信码错误 → 明确错误提示，不签发 token、不建账号。
- **FR-009**（2026-08-05 增量）: LINE「唤起 LINE App / 系统浏览器」流程下前端拿不到 code，新增服务端回调 `GET /auth/line/callback`（@Anonymous，LineCallbackController）：接 LINE 重定向的 code → 复用 verify("line",code) 换 token 取 userId → 已绑定签 token / 未绑定生成 tempKey → 302 跳回 App scheme `oauth.line.app-redirect`（`?token=` / `?needPhone=1&tempKey=` / `?error=`，未绑定时 App 再调 /infouser/user/oauthBindPhone）。原 /oauthLogin 保留，覆盖前端自取 code 场景（H5/webview/SDK）。约束：oauth.line.redirect-uri 须与 LINE Console 回调白名单一致（否则 400 redirect_uri_mismatch）。

## Key Entities

- **info_user_oauth（新增）**：`id`、`user_id`、`provider`(apple/google/line)、`provider_uid`、`create_time`。`UNIQUE(provider, provider_uid)` 用于按三方ID反查用户；`user_id` 普通索引。
- **InfoUser（已有，不改）**：仍是手机号为主键，无三方 ID 列。

## 安全要点

- Apple/Google 必须校验 audience（= 我方 client_id/bundleId），防其他 App 的 token 被冒用。
- Apple 校验 iss=`https://appleid.apple.com` + exp。
- tempKey 一次性、短 TTL（5 分钟），防重放。

## 不在本期范围

- 后台管理端解绑/查看三方绑定（后续增量）。
- 第三家以上三方（微信/Facebook）——表结构已可扩展，但本期只接 Apple/Google/LINE。
- Apple 隐藏邮箱中转、refresh_token 续期等高级场景。
