Skip to content

fix: 複数ターミナル使用時の refresh token rotation 競合を解消#93

Merged
miya0001 merged 2 commits into
mainfrom
fix/token-refresh-race
Apr 3, 2026
Merged

fix: 複数ターミナル使用時の refresh token rotation 競合を解消#93
miya0001 merged 2 commits into
mainfrom
fix/token-refresh-race

Conversation

@miya0001

@miya0001 miya0001 commented Apr 3, 2026

Copy link
Copy Markdown
Member

Summary

  • 複数ターミナルで同一プロファイルを使用時、refresh token rotation によりセッションが無効化される問題を修正
  • ClientOptionsonBeforeRefresh コールバックを追加し、トークンリフレッシュ前に config ファイルを再読み込み
  • 別プロセスが既にリフレッシュ済みの場合、サーバーへのリフレッシュリクエストをスキップして config のトークンを直接採用

Background

  • サーバー側で refresh token rotation が有効(リフレッシュ時に旧 refreshToken を無効化)
  • accessToken: 1時間、refreshToken: 7日
  • ターミナルAがリフレッシュすると、ターミナルBのメモリ上の refreshToken が無効化され再ログインが必要になっていた

Changes

  • src/types.ts: ClientOptionsonBeforeRefresh を追加
  • src/client.ts: doRefresh() 冒頭で onBeforeRefresh を呼び、config の最新トークンを取得
  • src/helpers.ts: createClient()onBeforeRefreshloadConfig() を接続
  • tests/client.test.ts: onBeforeRefresh のテスト2件追加

Test plan

  • lint / typecheck / 既存テスト全パス
  • onBeforeRefresh で別プロセスの更新済みトークンを採用するテスト
  • onBeforeRefresh で最新の refreshToken を使用するテスト
  • 複数ターミナルでの手動検証(次回トークン期限切れ時)

Summary by CodeRabbit

リリースノート

  • Bug Fixes

    • マルチターミナルで同じプロファイルを使う際、リフレッシュ前に設定を再読み込みし、既に更新されたトークンがあれば再利用、必要に応じて新しいリフレッシュトークンを採用することでセッション無効化を防ぐようにしました。
  • Tests

    • 上記の再読み込み/再利用動作とリフレッシュ挙動を検証するテストを追加しました。

複数ターミナルで同一プロファイルを使用している場合、一方のプロセスが
refresh token rotation でトークンを更新すると、他方のプロセスが保持する
古い refreshToken が無効化されセッション切れとなっていた。

トークンリフレッシュ前に config ファイルを再読み込みする
onBeforeRefresh コールバックを追加し、別プロセスが既にリフレッシュ済みの
場合はそのトークンを直接採用する。
@coderabbitai

coderabbitai Bot commented Apr 3, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 353aa240-bffc-4327-a428-d32d004d3d4d

📥 Commits

Reviewing files that changed from the base of the PR and between aefc233 and c4a04ef.

📒 Files selected for processing (1)
  • CHANGELOG.md
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md

📝 Walkthrough

Walkthrough

マルチターミナルで同じプロファイルを使った際に発生するリフレッシュ競合を防ぐため、GdbClientonBeforeRefreshフックを追加し、トークン刷新処理の直前に外部設定から最新のトークン情報を再読み込みして既に更新済みのトークンを再利用するようにしました。

Changes

Cohort / File(s) Summary
Type Definitions
src/types.ts
ClientOptionsonBeforeRefresh?: () => { token?: string; refreshToken?: string }を追加
Client Implementation
src/client.ts
GdbClientonBeforeRefreshを受け取るようコンストラクタを更新。doRefresh()開始時にフックを呼び出して最新トークン/refreshTokenを適用し、既存の刷新ロジックを続行
Helpers Integration
src/helpers.ts
CLIトークン使用時はフックを省略し、それ以外ではプロファイル設定を読み直してtoken/refreshTokenを返すonBeforeRefreshを提供するよう変更
Tests
tests/client.test.ts
onBeforeRefreshの振る舞いを検証するテストを追加(フックが新しいトークンを返した場合のリトライ成功、refreshTokenのみ更新された場合の/auth/refreshへの反映とエラー処理)
Changelog
CHANGELOG.md
## [Unreleased]配下に### 2026-04-03エントリを追加(リフレッシュ前に設定を再読込し既に更新済みのトークンを利用する旨を記載)

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client (GdbClient)
    participant Config as External Config
    participant Auth as Auth Server
    Client->>Config: onBeforeRefresh() を呼ぶ\n(最新 token/refreshToken を取得)
    alt Config has newer token
        Config-->>Client: { token: newToken }
        Client->>Client: this.token を更新してリクエスト再試行
        Client->>Auth: API リクエスト (新しい token で)
        Auth-->>Client: 200/成功
    else Config has new refreshToken only
        Config-->>Client: { refreshToken: newRefresh }
        Client->>Auth: POST /auth/refresh (body.refresh_token = newRefresh)
        alt refresh success
            Auth-->>Client: { token, refreshToken }
            Client->>Client: トークン保存/再試行
        else refresh fails
            Auth-->>Client: 401/失敗
            Client-->>Client: エラーを返す (GdbClientError)
        end
    end
Loading

推定レビュー工数

🎯 3 (Moderate) | ⏱️ ~25分

ポエム

🐰 端末がいくつあってもおまかせ
フックでそっと設定を覗きに行くよ
既に新しいトークンがあればすぐ使う
さあ並んで安全に走ろう
ぴょんと軽やか、セッション安泰

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PRタイトルは複数ターミナル使用時のrefresh token rotationの競合解消を説明しており、チェンジセットの主要な変更内容と完全に一致しています。
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/token-refresh-race

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CHANGELOG.md`:
- Line 11: The CHANGELOG entry for the fix ("**Fix**:
複数ターミナルで同一プロファイルを使用時、refresh token rotation によりセッションが無効化される問題を修正 — トークンリフレッシュ前に
config を再読み込みし、別プロセスが既にリフレッシュ済みならそのトークンを使用する") is missing the PR number suffix;
update that line in CHANGELOG.md to append the PR number in the standard format
(e.g. " (`#1234`)") at the end of the entry and ensure it sits under the correct
date group header.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4078861d-4d8e-4bb3-a70f-dde167d65ddb

📥 Commits

Reviewing files that changed from the base of the PR and between 3578338 and aefc233.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • src/client.ts
  • src/helpers.ts
  • src/types.ts
  • tests/client.test.ts

Comment thread CHANGELOG.md Outdated
@miya0001
miya0001 merged commit 4930149 into main Apr 3, 2026
7 checks passed
@miya0001
miya0001 deleted the fix/token-refresh-race branch April 3, 2026 07:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant