Skip to content

Periodic credential token refresh + dedupe Keychain service names - #32

Open
mvrska wants to merge 6 commits into
zacdcook:masterfrom
upcomers-org:feature/token-refresh
Open

Periodic credential token refresh + dedupe Keychain service names#32
mvrska wants to merge 6 commits into
zacdcook:masterfrom
upcomers-org:feature/token-refresh

Conversation

@mvrska

@mvrska mvrska commented Apr 10, 2026

Copy link
Copy Markdown

Summary

Two small improvements to credential handling:

  1. Periodic token refresh (proxy.js) — the proxy now checks token expiry every 5 minutes and, when less than 30 minutes remain, triggers a refresh by running `claude -p "ping" --max-turns 1 --no-session-persistence` and (on macOS) re-extracting from the Keychain into the snapshot file. Configurable via `refreshEnabled`, `refreshCheckMinutes`, `refreshThresholdMinutes` in `config.json`. Skipped automatically when running in `OAUTH_TOKEN` env-var mode. This avoids the ~24h manual refresh loop documented in the README.

  2. Dedupe Keychain service names — the list `['Claude Code-credentials', 'claude-code', 'claude', 'com.anthropic.claude-code']` was duplicated across `proxy.js`, `setup.js`, and `troubleshoot.js` (sometimes multiple times per file in both the loop and the log messages). Each file now defines a single `KEYCHAIN_SERVICES` constant at the top, and log messages derive from it via `.join(', ')`.

@zacdcook

Copy link
Copy Markdown
Owner

Thanks for this — the Keychain dedupe is clean and the auto-refresh concept directly addresses #6, which is a real pain point.

Before we move forward on the token refresh part, a question: have you been able to verify that the refresh actually triggers successfully at the 30-minute threshold? Specifically:

  1. Did you observe the [PROXY] Token expires in Xm, refreshing... log message fire?
  2. After it fired, did the token actually get refreshed (new expiry time in the log)?
  3. How long had the proxy been running continuously when the refresh triggered?

We tested claude -p ping on another system and found that when the token was still valid (7.9h remaining), the CLI used it but didn't write a new one to disk. The concern is whether the CLI actually refreshes preemptively at 30 minutes remaining, or only when the token is fully expired.

If you've confirmed it works end-to-end on your setup, that would give us the confidence to merge. The Keychain dedupe part we can cherry-pick regardless.

@DBostik

DBostik commented Apr 10, 2026

Copy link
Copy Markdown

Following this one closely. I was never able to get automatic token refresh to work as it constantly hit 429 errors.

Plus my token needs to be refreshed every 8 hours which is certainly an annoyance. Especially when using OC on a headless machine.

@mvrska

mvrska commented Apr 10, 2026

Copy link
Copy Markdown
Author

Thanks for this — the Keychain dedupe is clean and the auto-refresh concept directly addresses #6, which is a real pain point.

Before we move forward on the token refresh part, a question: have you been able to verify that the refresh actually triggers successfully at the 30-minute threshold? Specifically:

  1. Did you observe the [PROXY] Token expires in Xm, refreshing... log message fire?
  2. After it fired, did the token actually get refreshed (new expiry time in the log)?
  3. How long had the proxy been running continuously when the refresh triggered?

We tested claude -p ping on another system and found that when the token was still valid (7.9h remaining), the CLI used it but didn't write a new one to disk. The concern is whether the CLI actually refreshes preemptively at 30 minutes remaining, or only when the token is fully expired.

If you've confirmed it works end-to-end on your setup, that would give us the confidence to merge. The Keychain dedupe part we can cherry-pick regardless.

Hi, I was able to chat with openclaw today on multiple occasions since yesterdays restart of proxy.js.

So either there was some claude code call that I am not aware of by openclaw, which refreshed the token, or it worked 🤷🏻‍♂️

@mvrska

mvrska commented Apr 10, 2026

Copy link
Copy Markdown
Author
image

@mvrska

mvrska commented Apr 10, 2026

Copy link
Copy Markdown
Author
image

according to the log it looks like claude actually only refreshed the token when there was <1m time remaining? 👀

Michal Vrska and others added 4 commits April 10, 2026 23:11
The previous 30m threshold fired refreshes while Claude CLI still
considered the token valid, so five consecutive "refreshes" per cycle
were no-ops that just re-copied the same Keychain entry. Only the final
attempt (when the token was nearly expired) actually rotated it.

Drop the threshold to 2m and the check cadence to 15s so a refresh
fires once, just before real expiry, instead of spamming misleading
"refreshed" log lines. Also hoist the tunables and the sk-ant synthetic
expiry into top-of-file constants.
Revert the default check cadence back to 5m. When a refresh attempt
returns the same (or earlier) expiry -- meaning Claude CLI declined to
actually rotate -- reschedule the next check 15s out instead of waiting
another full interval. Successful rotations drop back to the normal 5m
cadence.

Switches from setInterval to a self-rescheduling setTimeout so the
cadence can vary per-tick, and adds a refreshRetrySeconds config knob.
Read the current expiresAt and arm a single setTimeout to fire exactly
when the token crosses the 2m threshold, instead of waking up every few
minutes to check. After a no-op refresh we still retry every 15s until
Claude CLI actually rotates.

Removes the now-unused refreshCheckMinutes config key.
@DBostik

DBostik commented Apr 11, 2026

Copy link
Copy Markdown

Just tested this manually myself using:

claude -p "ping" --max-turns 1 --no-session-persistence

Tested it every minute starting at 6 minutes remaining and checked /health.

It failed up until my token had <2 minutes remaining and then it worked.

So I'll probably have my agent create a job to refresh the token at 7 hours and 59 minutes.

@DBostik

DBostik commented Apr 11, 2026

Copy link
Copy Markdown

I can confirm again this morning from manual testing that using

claude -p "ping" --max-turns 1 --no-session-persistence

With <2 minutes remaining works to refresh token

@mvrska

mvrska commented Apr 13, 2026

Copy link
Copy Markdown
Author

we used this internally on two different OC instances and seem to have no issue with tokens

@DBostik

DBostik commented Apr 13, 2026

Copy link
Copy Markdown

we this internally on two different OC instances and seem to have no issue with tokens

Would you mind sharing your exact setup as this is something I'd love to finally solve.

The manual attempts I tried worked UNTIL Claude Code logged me out and I had to use 'Claude login' yesterday. Since then the manual attempts work again at <2 minutes.

So I think the fix would have to notify if a login message is encounter. Not sure how often Claude Code requires you to login or why it logs you out.

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.

3 participants