Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions deploy/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
35 changes: 29 additions & 6 deletions docs/PLAN_AUTH_OTP_GOOGLE_v1.0.md
Original file line number Diff line number Diff line change
@@ -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 已上线且处主位,义务已满足)。
Expand Down Expand Up @@ -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 已配齐)。
97 changes: 95 additions & 2 deletions docs/RUNBOOK_ACCOUNTS_LAUNCH.md
Original file line number Diff line number Diff line change
@@ -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、部署。按阶段顺序执行;
每步标注了前置依赖和大致耗时。

---
Expand Down Expand Up @@ -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='<web client id>.apps.googleusercontent.com' \
LISA_GOOGLE_IOS_CLIENT_ID='<ios client id>.apps.googleusercontent.com' \
LISA_CLOUD_APPLE_WEB_SID='<Apple Services ID,可选>' \
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` 时:
Expand All @@ -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)
```