Skip to content

fix: auto-refresh OIDC token with proactive + reactive strategy#17

Open
tumberger wants to merge 1 commit intomainfrom
04-09-fix_token_refresh_retry_on_401
Open

fix: auto-refresh OIDC token with proactive + reactive strategy#17
tumberger wants to merge 1 commit intomainfrom
04-09-fix_token_refresh_retry_on_401

Conversation

@tumberger
Copy link
Copy Markdown
Contributor

@tumberger tumberger commented Apr 9, 2026

Summary

Fixes #9 — OIDC access tokens expire during long coding sessions, causing heartbeats and event ingestion to silently fail.

What changed

Proactive refresh — The token source checks remaining TTL on every call. When less than 15 minutes remain (~75% of a typical 1h token), it refreshes ahead of time. This prevents latency spikes and avoids concurrent-refresh races on the heartbeat/event paths.

Reactive refresh (401 retry)bearerTransport.RoundTrip retries once with a forced token refresh on 401. Handles clock skew, server-side revocation, and edge cases where proactive refresh didn't fire in time.

Body consumption fix — The retry path now uses req.GetBody() to obtain a fresh request body. Previously, req.Clone() shared the original io.ReadCloser, so the retry would send an empty body (all ConnectRPC unary RPCs use POST).

Token persistence — Refreshed tokens are saved to the system keyring so other processes and subsequent kontext start runs see the new token.

Changes

  • internal/backend/backend.go: TokenSource takes forceRefresh bool, RoundTrip retries on 401 with fresh body
  • internal/run/run.go: newSessionTokenSource honors forceRefresh, shouldProactiveRefresh triggers at <15min remaining

Test plan

  • Run kontext start --agent claude for >1h, verify heartbeats continue after token expiry
  • Verify ✓ Token refreshed appears in stderr when proactive refresh fires
  • Verify 401 retry works by manually expiring the token server-side

🤖 Generated with Claude Code

devin-ai-integration[bot]

This comment was marked as resolved.

- Proactive refresh: refresh when <15min remains on the token TTL
  (~75% of a 1h token), preventing latency spikes on heartbeat/event paths
- Reactive refresh: retry once with forced refresh on 401 (handles
  clock skew, server-side revocation)
- Fix body consumption bug: use req.GetBody to obtain a fresh body for
  the retry request, since req.Clone shares the original io.ReadCloser
- Persist refreshed tokens to keyring so other processes see them

Closes #9

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tumberger tumberger force-pushed the 04-09-fix_token_refresh_retry_on_401 branch from ffd4aee to 293a865 Compare April 9, 2026 08:06
@tumberger tumberger changed the title fix: retry with forced token refresh on 401 Unauthenticated fix: auto-refresh OIDC token with proactive + reactive strategy Apr 9, 2026
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.

feat: auto-refresh OIDC access token during long-lived sessions

1 participant