Fix NIP-46 auth challenge timeout#2255
Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
nostter | dc0dbf9 | Commit Preview URL Branch Preview URL |
Jul 13 2026, 06:44 PM |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a5ee6c7e99
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| authTimer = setTimeout(() => { | ||
| rejectOnTimeout(new Error('NIP-46 authentication timed out')); | ||
| }, nip46AuthTimeout); |
There was a problem hiding this comment.
Cancel the stale signer branch on auth timeout
When this new auth timer rejects the Promise.race, the async signer.connect()/getPublicKey() branch below is not cancelled and can still resolve later. In the auth-timeout path, a user can retry or switch accounts and then complete the old auth challenge; that old branch can still assign the module-level nip46CachedPublicKey, leaving the current NIP-46 signer paired with a stale pubkey. Guard the later assignment with the settled/current-signer state or close/cancel the stale signer before rejecting.
Useful? React with 👍 / 👎.
|
@codex review |
| () => 'resolved', | ||
| () => 'rejected' | ||
| ), | ||
| Promise.resolve('pending') |
There was a problem hiding this comment.
「まだ pending であること」を確認しているつもりの
Promise.race([promise.then(...), Promise.resolve('pending')])
は、promise が既に reject 済みでも常に 'pending' を返します
There was a problem hiding this comment.
// before
await vi.advanceTimersByTimeAsync(nip46ConnectTimeout);
await expect(
Promise.race([
promise.then(() => 'resolved', () => 'rejected'),
Promise.resolve('pending')
])
).resolves.toBe('pending');
// after
let settled = false;
promise.then(
() => (settled = true),
() => (settled = true)
);
await vi.advanceTimersByTimeAsync(nip46ConnectTimeout);
expect(settled).toBe(false); // 接続タイムアウトでは終わっていないThere was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a5ee6c7e99
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| authTimer = setTimeout(() => { | ||
| rejectOnTimeout(new Error('NIP-46 authentication timed out')); | ||
| }, nip46AuthTimeout); |
There was a problem hiding this comment.
Clear auth timer once authentication succeeds
When an auth URL arrives this starts one timer that remains armed until both connect() and the follow-up getPublicKey() call finish. If the user completes the external auth near the 5-minute limit, connect() can resolve successfully but getPublicKey() is still racing an almost-expired auth timer, so the login can fail with NIP-46 authentication timed out even though authentication already succeeded. Clear or restart this timer when the authenticated request completes so the post-connect public-key request is not failed by the stale auth deadline.
Useful? React with 👍 / 👎.
gomurin0428
left a comment
There was a problem hiding this comment.
初回接続後のNIP-46 Auth Challenge処理に、後続の署名・暗号化操作を停止させる回帰があるため指摘します。
| bunkerSigner = BunkerSigner.fromBunker(clientSeckey, bunkerPointer, { | ||
| onauth: (url) => { | ||
| authRequested = true; | ||
| if (settled) { |
There was a problem hiding this comment.
[P1] 接続後の正当な Auth Challenge を無視しないでください
settled は初回の接続処理が正常終了した後も true のままですが、この onauth コールバックは接続処理専用ではなく、長寿命の BunkerSigner が行う後続の sign_event / encrypt など全リクエストで再利用されます。nostr-tools@2.23.5 は各 sendRequest() で Auth Challenge を受け付け、NIP-46 も sign_event での challenge を明示しています。そのため、接続後にユーザー承認が必要な署名要求が来ると URL を開かずに return し、ユーザーが承認できないまま最終応答を無期限に待ちます。成功した signer ではこのコールバックを有効なままにし、失敗・破棄された接続試行だけを無効化してください。
Motivation
establishBunkerConnection()がonauthを受け取った後に既存の接続タイマーでrejectしなくなり、ユーザーが外部で認証を完了しないとログイン処理が無期限待機する不具合を修正するため。Description
web/src/lib/Constants.tsに新しい定数nip46AuthTimeout = 5 * 60 * 1000を追加して、Auth Challenge 後の待機上限を定義した。web/src/lib/signer-strategy.tsのestablishBunkerConnection()を接続/認証で別々のタイマー管理に差し替え、connectTimer(接続タイムアウト)とauthTimer(Auth Challenge 後タイムアウト)を導入し、終了済みを示すsettledフラグとclearTimers()を実装して不要な再起動や解除漏れを防止した。NIP-46 connection timed out、認証待機タイムアウトはNIP-46 authentication timed outを投げるようにした。web/src/lib/signer-strategy.test.tsに最小限のnostr-tools/nip46モックと fake timers を用いたテストを追加し、接続タイムアウト、Auth Challenge 後のタイムアウト、Auth Challenge 後の正常完了、終了後に来る遅延onauthの無視 を検証するテストを追加した。Testing
npm testを実行し、プロジェクトのテストスイートは17 files / 165 testsすべてが通過した(成功)。npm run checkを実行し、svelte-checkは0 errors and 0 warningsを報告した(成功)。npm run lintを実行し、prettierとeslintのチェックが通った(成功)。connect()/getPublicKey()が完了する場合の正常終了、および接続終了後に届く遅延onauthの無視。Codex Task