ADR-0104: 失効しない GAS/clasp デプロイ認証 — OAuth client の組織 Internal 化
- Status: Accepted
- Mode: Standard
- Kruchten Type: Executive/Property
- Scope: platform
- Implementation Status: 決定① Done (2026-06-01 実機完了・dev
clasp push88 ファイル成功) / 決定② Not Started (ADC キーレス化 PoC・後段ゲート・任意) - 起案者: [email protected]
- 起案日時 (JST): 2026-06-01
- 承認日時 (JST): 2026-06-01
- Deciders: [email protected] (単独)
Pipeline 迂回 + 遡及記録の経緯(監査用注記): 本 ADR は Decision Pipeline を経由せず通常 PR で起案した遡及 (retroactive) ADR である。元は認証・秘密情報の全体方針を 1 本に束ねた単一 ADR (KV_DRAFT
auth-secret-consolidation-strategy) として起案したが、Cross-Validation で Critical 38/50 差し戻しとなり、主因が「独立決定 7 件を 1 ADR に束ねて cost/impact/baseline が曖昧化した」ことだったため、代表取締役判断で課題を 3 テーマに分割した (2026-06-01)。本 ADR はそのうちテーマ①「失効しない GAS/clasp デプロイ認証」に対応する。決定① (OAuth client の組織 Internal 化) は 2026-06-01 に実機で実施・検証済み (devclasp push88 ファイル成功・CLASPRC_JSONsecret 更新済) であり、本 ADR は実施済みの意思決定を監査トレースとして事後記録するもの。残るテーマ② (pipeline Basic 認証→Cloudflare Access)・テーマ③ (静的 secret 単一ソース化 + GitHub OIDC/WIF キーレス化 + pre-flight 検知) は本 ADR スコープ外で、TODO_future の backlog として追って別 ADR 化する。Pipeline retroactive validation (ADR-0052) は任意。
コンテキスト
§1.1 何を解決するか
clasp の OAuth refresh token が予告なく失効し、GAS デプロイ(ローカル clasp / CI「Deploy to GAS」)が破綻する。CLAUDE.md に「clasp 認証切れ (invalid_grant) は自律実行不可 → ユーザに ! clasp login --creds creds.json を依頼」と明記される運用劣化が常態化していた。
§1.2 現状 (As-Is) と実測ベースライン
GitHub Actions deploy.yml は直近 60 run が 60 失敗(100%・2026-05-29〜06-01)。直近 8 件はすべて Requested entity was not found@clasp push で、invalid_grant 文字列は表面に出ていなかった。調査の結果、この Requested entity not found は旧 OAuth client(後述)の refresh token 失効が紛らわしく表面化したものと判断した。
§1.3 真因 (2026-06-01 特定)
clasp の OAuth client(creds.json)が、Google AI Studio が自動生成した野良プロジェクト gen-lang-client-0297558711 に存在していた。このプロジェクトは bizlp.co.jp 組織配下ではなく、OAuth 同意画面が必然的に External + Testing 固定 → refresh token が 7 日で失効する。これが invalid_grant / 失効の構造的原因。
§1.4 制約・スコープ
本 ADR のスコープは clasp/GAS デプロイ認証に限定する。decision-pipeline の Basic 認証(→ Cloudflare Access)、静的シークレット集約・CI キーレス化(Secrets Store / WIF)は、それぞれ別テーマ(テーマ②/③・TODO_future backlog)に分割した。invalid_grant の真因は 7 日失効以外にも多原因(パスワード/2FA 変更・client secret rotation・GitHub Secret の base64 崩れ・複数環境同時リフレッシュ race)があり、Internal 化はこれらを消さないため「失効撲滅」と断言せず pre-flight 検知(テーマ③)との併用前提とする。
§1.5 目標 (To-Be)
clasp OAuth client を組織配下の Internal client に移行し、7 日失効を構造的に撲滅する。CI / ローカル shell の双方で予告なきデプロイ失敗を解消し、client の所在を組織配下に一元化する。
決定
clasp OAuth client を野良 External プロジェクト gen-lang-client-0297558711 から、組織配下 GCP プロジェクト bizlp-gas-accounting-dev の Internal ユーザータイプ client へ作り直す。2 決定を ROI 順で適用する。
- 決定① Internal 化(実証済み・低リスク・本 ADR の中核): 組織配下
bizlp-gas-accounting-devに Internal OAuth client を新規作成 →creds.json差替 → 再認証 → devclasp pushで疎通確認。Internal は (i) 7 日失効なし (ii) Google 審査・CASA 不要 (iii) 外部に OAuth フロー入口が開かない(露出増ゼロ)。2026-06-01 に実機で実施・devclasp push88 ファイル成功・CLASPRC_JSONsecret 更新済。 - 決定② clasp v3 ADC キーレス化 PoC(任意・後段ゲート): refresh token 自体を Application Default Credentials で撲滅できるか実機検証。3 モデル Deep Research で可否が割れた(Claude/OpenAI=不可、Gemini=可)ため一括前提にせず、決定①の完了・効果確認の後に着手。「実機 2 回失敗 または カレンダー工数 1.5 人日超」で自動撤退し、Internal client 運用を最終形として維持する。
決定①を先・決定②を後に固定するのは、確実に効く①を不確実な②に引きずられて後回しにしないため(元レビュー盲点 #12 への構造的回答)。
判断基準 (Decision Drivers)
§3.1 評価軸
| # | 軸 | 重要度 (係数) | 案件特有の解釈 |
|---|---|---|---|
| 1 | #secure | [Must] (×2.0) | 静的・長寿命鍵の露出面を増やさない。K.O. = 現状より露出面が増えるなら不採用。→ Internal 化は外部入口を開かず露出増ゼロでクリア。残る長寿命トークン漏洩リスクは .clasprc.json/CLASPRC_JSON 保護 + 将来テーマ③で対処 |
| 2 | #operable | [High] (×1.0) | デプロイを予告なく失効させない。K.O. = clasp デプロイを恒久的に壊すなら不採用 |
| 3 | #reliable | [High] (×1.0) | CI / ローカル shell の両方で安定動作(sub サンドボックスは本 ADR スコープ外=テーマ②) |
| 4 | #efficient | [Medium] (×0.5) | コスト増を許容しない($0 維持)。Internal は審査費(CASA Tier2 数十万円規模)も回避 |
| 5 | #maintainable | [Medium] (×0.5) | client が組織配下に一元化され、所在が明確 |
K.O. criterion: Must 軸 (#secure) score < 3 は不採用。
§3.2 評価軸 × 案スコア表
| 軸 | 係数 | 採択案 (組織 Internal 化 + ADC PoC 後段) | 案A (External 公開 publish) | 案B (現状維持 + 手動再認証) | 案C (clasp 完全キーレス化を一括前提) |
|---|---|---|---|---|---|
#secure | ×2.0 | 5 | 2 | 3 | 5 |
#operable | ×1.0 | 5 | 4 | 1 | 3 |
#reliable | ×1.0 | 4 | 3 | 1 | 2 |
#efficient | ×0.5 | 5 | 2 | 4 | 4 |
#maintainable | ×0.5 | 5 | 3 | 2 | 4 |
| 加重和 (正規化) | 0.960 | 0.540 | 0.440 | 0.760 | |
| K.O. 通過 (Must ≥3) | ✓ | ❌ | ✓ | ✓ |
加重和 = Σ(score × 係数) / (5 × Σ係数)、Σ係数 = 5.0。採択案は実機で決定①を完了済みのため #operable/#secure を実証値で 5、残る長寿命トークン漏洩リスクを反映し #reliable は 4。案A は External 公開が外部に OAuth フロー入口を開き #secure=2 で K.O. 不通過。案C は可否が 3 モデルで割れ #reliable=2 と不確実なため、決定②として段階化(採択案に内包)した。
検討した代替案 (Alternatives Considered)
- 採択案(組織 Internal 化 + ADC PoC 後段): 上記決定①②。露出増ゼロ・審査不要・$0 で 7 日失効を構造的に撲滅。実機で決定①完了済み。
- 案A(External のまま consent screen を "In production" に publish): 7 日失効は外せるが、External 公開は任意の Google アカウントに OAuth フロー入口が開き露出面が増える + 制限付きスコープ次第で Google 審査/CASA(数十万円)を発動しうる。不採用 = Internal 化が上位互換(露出増ゼロ・審査不要)。組織配下でなければこれが唯一手だが、bizlp は Workspace 組織を保有し
bizlp-gas-accounting-devが組織配下と確認できたため不要。 - 案B(現状維持 + 手動再認証): 改修ゼロ。不採用 = 7 日ごとの手動
clasp loginと予告なき CI 失敗が継続、自律実行性を損なう。 - 案C(clasp 完全キーレス化を一括前提): 不採用 = 可否が 3 モデルで割れ、PoC で確定すべき。決定①を確実に取った上で決定②として段階検証する。
影響 (Consequences)
§5.1 正の影響 (Good)
- 7 日失効を構造的に撲滅(Internal)。
CLASPRC_JSON起因の CI 失敗を解消見込み。 - client が組織配下に一元化され所在が明確。コスト増なし・審査不要。
§5.2 負の影響 (Bad)
- client 作り直し + 再認証の初期工数(実績 ≈0.3〜0.5 人日)。
- Internal 化で「7 日自動失効という漏洩時の自動安全弁」は外れるため、長寿命トークン(
~/.clasprc.json/CLASPRC_JSON)漏洩時の blast radius は拡大 → コミット禁止 hook(既設)+ secret 最小権限 + 将来テーマ③(ADC キーレス化)で対処。 - 影響ファイル/リソース:
.github/workflows/deploy.yml(secretCLASPRC_JSON)/~/.clasprc.json/ リポジトリcreds.json/ GCP プロジェクトbizlp-gas-accounting-devの OAuth client(新規 Internal・デスクトップアプリ)/ 旧gen-lang-client-0297558711の client(フォールバックとして当面残置、検証後に削除)。※ wrangler・decision-pipeline・Keychain は本 ADR では触らない(テーマ②/③)。
§5.3 監査・実機結果 (2026-06-01)
Internal 化(sub)+ secret stale 解消(main)により CLASPRC_JSON 起因の CI 失敗は実機解消済(dev/prod CI green)。clasp 3.3.0 の .clasp.json cp 切替は正常動作と実証(cp dev→dev / cp prod→prod、push:prod は正しく prod を対象 = 監査問題なし)。残課題 B(CLASP_DEPLOYMENT_ID_* secret)も登録済。GAS/clasp デプロイ認証の課題はクローズ方向。
コスト試算
| 作業 | 工数 (人日) | 備考 |
|---|---|---|
| 決定① Internal 化(GCP で Internal client 作成 + creds 差替 + 再認証 + Apps Script API 有効化 + dev 疎通) | 0.3〜0.5(実績) | 実機完了 2026-06-01 |
| 決定② ADC キーレス化 PoC | ≤1.5(撤退上限) | 任意・後段ゲート |
| 合計 | 約 2 人日以内 |
- 運用コスト: $0/月(GCP OAuth client 無料・追加サービスなし)。
- 審査コスト: $0(Internal は Google OAuth verification / CASA 対象外)。← External publish 案なら CASA Tier2 数十万円規模のリスクがあった。
撤退条件 (Rollback Plan)
| # | 判定指標 | 検知方法 | 代替アクション |
|---|---|---|---|
| 1 | 決定② ADC PoC が実機 2 回連続失敗 または カレンダー工数 1.5 人日超 | PoC 実施ログ | 自動撤退 → Internal client 運用(決定①)を最終形として維持 |
| 2 | Internal 化後も clasp 認証が壊れる | clasp push 失敗 / CI red | バックアップから即復旧: cp ~/creds.json.bak creds.json && cp ~/.clasprc.json.bak ~/.clasprc.json(所要 1 分未満)。旧 gen-lang client もフォールバックとして当面残置 |
| 3 | 長寿命トークン漏洩の兆候 | secret スキャン / hook 検知 | secret 即 rotation + テーマ③(ADC キーレス化)を前倒し |
検証完了後(prod CI green 確認後): 旧 gen-lang client と $HOME のバックアップ(~/creds.json.bak / ~/.clasprc.json.bak)を削除し、露出面・平文認証の長期放置を排除する。
Confirmation
- 検証手段:
- ローカル
clasp push -f(dev)の成功(2026-06-01 実証済み・88 ファイル)。 - CI「Deploy to GAS」が green になること(dev/prod 実機 green 確認済)。
- pre-flight 失効検知(テーマ③と連携・将来)。
- ローカル
- 実行頻度: デプロイ時の手動確認 + CI run ごと。
- 観測可能 KPI:
- invalid_grant・
Requested entity not found起因の CI デプロイ失敗を月 0 件(ベースライン: 直近 60/60 失敗)。 - 手動
clasp login実施回数を月 0 回(ベースライン: 7 日ごと)。
- invalid_grant・
- 違反時対応: §撤退条件を発動。決定② は撤退条件 #1 で自動撤退し Internal client 運用へ戻す。
参照 (References)
- 関連 ADR:
- ADR-0013(Env module abstraction): 補完。秘密アクセスの抽象化方針と整合(本 ADR は GAS デプロイ認証に限定)。並立・補完
- ADR-0019(decision-pipeline langgraph migration, Accepted): 関連。pipeline の Basic 認証置換は本 ADR ではなくテーマ②で扱う。並立
- ADR-0037(serverside draft staging lifecycle, Accepted)/ ADR-0064(CI trigger web→CI, Accepted): 関連(CI 認証面)。いずれも Supersede しない・補完。
- ADR-0052(retroactive validation mode): 本 ADR は実施済み決定の遡及記録であり、Pipeline retroactive validation は任意。
- Supersede / Conflict: 元の単一 ADR(KV_DRAFT
auth-secret-consolidation-strategy・Critical 38/50 差し戻し)を、本 ADR(テーマ①)+ テーマ②/③(TODO_future backlog)への分割で置換。当該 KV draft は本セッションで DELETE 済。 - 関連調査: RQ-085「認証・秘密情報の統合管理」3 モデル Deep Research(
docs/research/rq-085-auth-secret-consolidation.mdほか)。決定②の ADC 可否が割れた根拠もここに記録。 - 将来 backlog: テーマ②(pipeline Basic 認証→Cloudflare Access Service Token)/ テーマ③(静的 secret 単一ソース化 + GitHub OIDC/WIF キーレス化 + pre-flight 検知)は TODO_future に backlog 化し、別 ADR として追って起案する。
- バックアップ手順: 移行前に
~/creds.json.bak/~/.clasprc.json.bakを $HOME(git 管理外)に取得済。