From 7670028f5cbfccac8212e23848b2e84ba3005c42 Mon Sep 17 00:00:00 2001 From: oratis Date: Fri, 24 Jul 2026 00:28:32 +0800 Subject: [PATCH] docs: operator steps for sign-in by code and Google, and the env to deploy them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit deploy.sh passes an explicit env allowlist, so the two Google client ids would have been dropped silently — a deploy that looked fine and left the button invisible. Added, with the sign-in-by-code dependency on RESEND_API_KEY spelled out in the header (no key ⇒ codes only reach the server log ⇒ nobody can sign in). Runbook gains Phase 8 (verify the Resend sending domain, with a curl self-check), Phase 9 (GCP OAuth consent + the Web and iOS client ids, plus the redeploy command with both new env groups) and Phase 10 (ship a build — deliberately NOT prescribing a version bump, since whether this folds into 1.1 or becomes 1.2 depends on ASC state the repo can't see). Plan doc marked implemented with the PR chain. Co-Authored-By: Claude Fable 5 --- deploy/deploy.sh | 13 +++++ docs/PLAN_AUTH_OTP_GOOGLE_v1.0.md | 35 +++++++++-- docs/RUNBOOK_ACCOUNTS_LAUNCH.md | 97 ++++++++++++++++++++++++++++++- 3 files changed, 137 insertions(+), 8 deletions(-) diff --git a/deploy/deploy.sh b/deploy/deploy.sh index 3ea7340..04a8403 100755 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -25,6 +25,13 @@ # LISA_CLOUD_APPLE_SUBS=… optional comma-separated Apple `sub` allowlist # LISA_CLOUD_APPLE_AUD=… override the expected bundle id (default ai.meetlisa.main) # +# Optional — Sign in with Google (off unless set; src/web/googleAuth.ts): +# LISA_GOOGLE_WEB_CLIENT_ID=… OAuth "Web application" client id (GIS button) +# LISA_GOOGLE_IOS_CLIENT_ID=… OAuth "iOS" client id (the app's PKCE flow) +# +# Sign-in by mailed code (PLAN_AUTH_OTP_GOOGLE A1) needs RESEND_API_KEY below — +# without it codes are only logged server-side and nobody can complete a sign-in. +# # Usage (GLM): # LISA_WEB_TOKEN=… ZHIPU_API_KEY=… deploy/deploy.sh # # …with iOS sign-in: append LISA_CLOUD_APPLE_SIGNIN=1 @@ -61,6 +68,12 @@ ENVS="^##^LISA_EDITION=cloud##LISA_WEB_TOKEN=${LISA_WEB_TOKEN}" # Sign in with Apple on the WEB login page (B8b): the Services ID registered in # the Apple portal for cloud.meetlisa.ai (needs domain verification there). [ -n "${LISA_CLOUD_APPLE_WEB_SID:-}" ] && ENVS="${ENVS}##LISA_CLOUD_APPLE_WEB_SID=${LISA_CLOUD_APPLE_WEB_SID}" +# Sign in with Google (PLAN_AUTH_OTP_GOOGLE A3/A4): the OAuth client ids from the +# GCP console — Web application (the GIS button) and iOS (the app's PKCE flow). +# Each surface draws its button only when its own id is here. Both unset ⇒ Google +# sign-in is off everywhere and /api/auth/google 404s. +[ -n "${LISA_GOOGLE_WEB_CLIENT_ID:-}" ] && ENVS="${ENVS}##LISA_GOOGLE_WEB_CLIENT_ID=${LISA_GOOGLE_WEB_CLIENT_ID}" +[ -n "${LISA_GOOGLE_IOS_CLIENT_ID:-}" ] && ENVS="${ENVS}##LISA_GOOGLE_IOS_CLIENT_ID=${LISA_GOOGLE_IOS_CLIENT_ID}" # Accounts & billing era (PLAN_ACCOUNTS_BILLING B1–B7), all optional: # LISA_REVIEWER_SEED="email:password" idempotent App-Review demo account (verified, $20/Tier-2) # LISA_RPM_LIMIT / LISA_DAILY_CAP_USD abuse guards (defaults 20 rpm / $200 per day) diff --git a/docs/PLAN_AUTH_OTP_GOOGLE_v1.0.md b/docs/PLAN_AUTH_OTP_GOOGLE_v1.0.md index 6efbf95..be82f85 100644 --- a/docs/PLAN_AUTH_OTP_GOOGLE_v1.0.md +++ b/docs/PLAN_AUTH_OTP_GOOGLE_v1.0.md @@ -1,6 +1,7 @@ # PLAN — 邮箱验证码登录 + Google 登录 / Email-OTP & Google Sign-In (v1.0) -**Status: DRAFT,待评审。** 承接 [PLAN_ACCOUNTS_BILLING_v1.0.md](PLAN_ACCOUNTS_BILLING_v1.0.md) +**Status: IMPLEMENTED(代码侧全部落地,2026-07-24;等运营配置见 §4/§6)。** +承接 [PLAN_ACCOUNTS_BILLING_v1.0.md](PLAN_ACCOUNTS_BILLING_v1.0.md) (B1 邮箱+密码 / SIWA、B8a 验证链接、B8b SIWA-web)。目标:① 邮箱**验证码(OTP) 免密登录**成为邮箱路径的默认形态;② 新增 **Google 登录**(推翻原 §4 "不加 Google" 的裁决——当时的顾虑是 4.8 连带义务,但 SIWA 已上线且处主位,义务已满足)。 @@ -87,9 +88,31 @@ $5"完全同级,不新增暴露(仍受 per-uid 并发=1 + RPM + 全局日上限 (顺带补 SIWA-web 的 Services ID,把 `appleWeb:null` 一起解决)。 4. ASC:1.2 送审时 Review Notes 不变(demo 账号仍密码制);4.8 合规自查通过。 -## 5. 决策点(默认已选推荐项,评审时可推翻) +## 5. 决策(2026-07-24 拍板,均按推荐项执行) -1. Google 邮箱与既有邮箱账号**自动绑定**(推荐)vs 各自建号。 -2. iOS 用 **PKCE 零依赖**(推荐)vs 引 GoogleSignIn SPM。 -3. Mac/CLI 的 Google **缓到 v1.1**(推荐)vs 一并做。 -4. OTP 默认位(推荐 OTP-first,密码折叠)vs 密码默认、OTP 备选。 +1. Google 邮箱与既有邮箱账号**自动绑定** —— 一个地址一个账号,余额不分裂(§2.2)。 +2. iOS 用 **PKCE 零依赖**(ASWebAuthenticationSession),不引 GoogleSignIn SPM。 +3. Mac/CLI 的 Google **缓到下一版**;两端已有 OTP,免密诉求已满足。 +4. **OTP 为默认**,密码折叠进 "Use a password instead"(审核账号仍走密码)。 + +## 6. Implementation log (2026-07-24) + +堆叠 PR 链(base: main ← #289 ← #290 ← #291 ← #292 ← #293): + +| 里程碑 | PR | 内容摘要 | +| --- | --- | --- | +| A0+A1 | #289 | 本方案入库 + OTP 服务端(otp.ts 存储/两端点/邮件/限流) | +| A3 | #290 | Google 验签(googleAuth.ts)+ /api/auth/google + 一地址一账号绑定 | +| A2 | #291 | OTP 四端(web code-first / iOS 共用表单 / Mac / CLI `lisa login`) | +| A4 | #292 | Google 客户端(web GIS + iOS PKCE)+ config 下发 client id | +| A5 | #293 | 本节 + runbook 增补(RUNBOOK_ACCOUNTS_LAUNCH Phase 8/9) | + +**顺带修掉的既有问题**(A2 实测时暴露):CLI 每个 prompt 新建 readline 会丢弃上一个 +reader 的缓冲(非 TTY 下第二个 prompt 永远读空);`lisa login` 的兜底 catch 把任何 +异常都报成"连不上服务器"。另:登录页首次纳入 inline-script 语法守卫; +`lisa --help` 首次列出 login/logout/billing。 + +**仍待人工(operator)**:见 §4 与 +[RUNBOOK_ACCOUNTS_LAUNCH.md](RUNBOOK_ACCOUNTS_LAUNCH.md) Phase 8–9 —— +Resend 发信域核实(**OTP 硬依赖**)、GCP OAuth consent + 两个 client ID、 +带新 env 重部署、iOS 1.2 打 TestFlight(`git tag pocket-v1.2.0`,CI 已配齐)。 diff --git a/docs/RUNBOOK_ACCOUNTS_LAUNCH.md b/docs/RUNBOOK_ACCOUNTS_LAUNCH.md index b594e65..0d9481d 100644 --- a/docs/RUNBOOK_ACCOUNTS_LAUNCH.md +++ b/docs/RUNBOOK_ACCOUNTS_LAUNCH.md @@ -1,7 +1,9 @@ # RUNBOOK — 账号/计费上线的人工操作 (operator actions) -**配套**: [PLAN_ACCOUNTS_BILLING_v1.0.md](PLAN_ACCOUNTS_BILLING_v1.0.md) §10(PR #259–#267)。 -本文是代码做不了的那部分:Apple 后台、ASC、DNS、部署。按阶段顺序执行; +**配套**: [PLAN_ACCOUNTS_BILLING_v1.0.md](PLAN_ACCOUNTS_BILLING_v1.0.md) §10(PR #259–#267) +与 [PLAN_AUTH_OTP_GOOGLE_v1.0.md](PLAN_AUTH_OTP_GOOGLE_v1.0.md) §6(PR #289–#293, +验证码登录 + Google 登录 → Phase 8–10)。 +本文是代码做不了的那部分:Apple 后台、ASC、GCP、DNS、部署。按阶段顺序执行; 每步标注了前置依赖和大致耗时。 --- @@ -201,6 +203,93 @@ app now verifies the connection at sign-in time. Please review 1.1. - 急停:`gcloud run services update lisa-cloud --update-env-vars LISA_BILLING_KILL=1 …` (恢复时改回空值)。 +## Phase 8 — 验证码登录上线(Resend 是硬依赖) + +> 配套 [PLAN_AUTH_OTP_GOOGLE_v1.0.md](PLAN_AUTH_OTP_GOOGLE_v1.0.md) A1/A2。 +> **没有可用的发信通道 = 没人收得到验证码 = 登录不了**(缺 key 时验证码只会 +> 打进服务端日志)。上线前务必确认这一条。 + +1. **Resend**:确认 `RESEND_API_KEY` 已在 Cloud Run env 里,且 `meetlisa.ai` + 在 Resend 后台是 **Verified**(SPF + DKIM 记录已加在 DNS)。 + B8a 的验证邮件用的是同一条通道,所以之前若已验证过就无需重做。 +2. **自检**(部署后): + +```bash +BASE=https://cloud.meetlisa.ai +# 应答 {"ok":true,"sent":true,...};sent:false 就是发信没配好 +curl -s $BASE/api/auth/otp/request -H 'content-type: application/json' \ + -d '{"email":"你的邮箱@example.com"}' +# 收到邮件后拿六位数字换 session +curl -s $BASE/api/auth/otp/verify -H 'content-type: application/json' \ + -d '{"email":"你的邮箱@example.com","code":"123456"}' +``` + +3. 免费额度:验证码登录建号即 `verified=true`,直接拿满额 $5 窗口(旧的 + "未验证邮箱 $1" 只剩历史密码账号会遇到)。 +4. 审核账号不受影响:`reviewer@meetlisa.ai` 仍是密码登录,ASC 表单不用改。 + +## Phase 9 — Google 登录(GCP OAuth) + +> 配套 A3/A4。不配 client ID 时,Google 按钮在四端都不出现,其它登录方式照常。 + +1. GCP Console(项目 `oratis-491316`)→ **APIs & Services → OAuth consent + screen**:User Type 选 **External**,填应用名/支持邮箱/开发者邮箱, + Scopes 只要 `openid`、`email`(不要 profile 之外的敏感 scope,免走审核), + 然后 **Publish app**(Testing 状态只有测试名单里的账号能登)。 +2. **Credentials → Create Credentials → OAuth client ID**,建**两个**: + - **Web application** —— Authorized JavaScript origins 填 + `https://cloud.meetlisa.ai`(GIS 按钮按 origin 校验;不需要 redirect URI)。 + - **iOS** —— Bundle ID 填 `ai.meetlisa.main`。 + (iOS 客户端的 redirect 由 Google 自动按"反转 client ID"配好,无需手填; + app 侧也不用注册 URL scheme —— ASWebAuthenticationSession 自己拦截回调。) +3. **重新部署**,带上两个 client ID(顺带把 SIWA-web 的 Services ID 一起补上, + 解决 `/api/auth/config` 里 `appleWeb: null`): + +```bash +LISA_WEB_TOKEN='<现有值>' \ +ZHIPU_API_KEY='<现有值>' \ +LISA_MODEL=glm-4.6 \ +LISA_CLOUD_APPLE_SIGNIN=1 \ +LISA_REVIEWER_SEED='reviewer@meetlisa.ai:<现有密码>' \ +RESEND_API_KEY='<现有值>' \ +LISA_GOOGLE_WEB_CLIENT_ID='.apps.googleusercontent.com' \ +LISA_GOOGLE_IOS_CLIENT_ID='.apps.googleusercontent.com' \ +LISA_CLOUD_APPLE_WEB_SID='' \ +deploy/deploy.sh +``` + +4. **自检**:`curl -s https://cloud.meetlisa.ai/api/auth/config` 应看到 + `google: {webClientId: …, iosClientId: …}`;打开登录页应出现 Google 按钮。 +5. **iOS 1.2 送审前**:Google 登录不改变 App Privacy 申报(email/User ID 已申报); + 4.8 合规靠 SIWA 仍在首位满足 —— 改动版面时别把 Apple 按钮挪到 Google 下面。 + +## Phase 10 — 发 iOS 构建(CI 已配齐,无需 Mac) + +**先确认版本号该不该动**(取决于 ASC 上 1.1 的状态,代码里看不出来): + +- **1.1 还没送审**(Phase 6 尚未做完)→ **不要改版本号**。验证码/Google 登录 + 直接并进 1.1 这一版一起送审,只是多打一个 build。 +- **1.1 已在审核中或已上架** → 把 `packaging/ios-companion/project.yml` 的 + `MARKETING_VERSION` 提到 `1.2`,再走下面的 tag。 + +六个签名 secret 自 2026-07-23 起已在 `oratis/LISA` 仓库配好,任何有 push 权限的 +协作者都能发版(tag 名与 MARKETING_VERSION 对齐即可): + +```bash +git tag pocket-v1.1.1 && git push origin pocket-v1.1.1 +``` + +(或 Actions 里手动跑 `release-ios-testflight.yml`。)build number 是构建时的 +Unix 时间戳,自动生成,所以同一 MARKETING_VERSION 可以反复出 build。细节见 +[packaging/ios-companion/RELEASE.md](../packaging/ios-companion/RELEASE.md)。 + +**Review Notes 增补一句**(Phase 6c 那段文案后面加): + +``` +Sign-in now also accepts a one-time code emailed to any address, and +Sign in with Google. The demo account above still uses email + password. +``` + ## (可选)多实例扩容 — Firestore 模式(B9) 单实例(默认)什么都不用做。用户量上来后要 `max-instances > 1` 时: @@ -225,4 +314,8 @@ Phase 0 (merge) ─┬─► Phase 1 (SIWA capability) ──► Phase 6b (build ├─► Phase 2 (Paid Apps 协议) ───► Phase 4 (IAP 商品) ─► Phase 6d (随 1.1 送审) ├─► Phase 3 (DNS + deploy) ─────► Phase 6c (审核账号可用) └─► Phase 5 (SBP,独立) + +验证码 / Google(PR #289–#293 合并后): +Phase 8 (Resend 核实) ──┐ +Phase 9 (GCP OAuth) ────┴─► 一次重部署(两组 env 一起带上)──► Phase 10 (发 1.2) ```