-
Notifications
You must be signed in to change notification settings - Fork 344
vault set treats relative expires_in as immediately due and can invalidate fresh grants #334
Copy link
Copy link
Closed
Labels
P1Urgent regression or broken agent/channel workflow affecting real users now.Urgent regression or broken agent/channel workflow affecting real users now.clawsweeper:current-main-reproClawSweeper found a high-confidence current-main issue reproduction.ClawSweeper found a high-confidence current-main issue reproduction.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.ClawSweeper found an open linked pull request for this issue.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.ClawSweeper does not recommend queueing a new automated fix PR for this issue.impact:auth-providerThis issue is about auth, provider routing, model choice, or SecretRef resolution.This issue is about auth, provider routing, model choice, or SecretRef resolution.issue-rating: 🦀 challenger crabExceptional issue quality: high-confidence current-main reproduction and actionable evidence.Exceptional issue quality: high-confidence current-main reproduction and actionable evidence.
Description
Activity
Metadata
Metadata
Assignees
Labels
P1Urgent regression or broken agent/channel workflow affecting real users now.Urgent regression or broken agent/channel workflow affecting real users now.clawsweeper:current-main-reproClawSweeper found a high-confidence current-main issue reproduction.ClawSweeper found a high-confidence current-main issue reproduction.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.ClawSweeper found an open linked pull request for this issue.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.ClawSweeper does not recommend queueing a new automated fix PR for this issue.impact:auth-providerThis issue is about auth, provider routing, model choice, or SecretRef resolution.This issue is about auth, provider routing, model choice, or SecretRef resolution.issue-rating: 🦀 challenger crabExceptional issue quality: high-confidence current-main reproduction and actionable evidence.Exceptional issue quality: high-confidence current-main reproduction and actionable evidence.
Type
Fields
Priority
None yet
Summary
mcporter vault setpersists an OAuth token carryingexpires_inwithout converting that relative lifetime to an absolute expiry. On the nextlistorcall, mcporter treats the imported token as immediately due and redeems its refresh token even when it was issued seconds ago with a one-hour lifetime.This can destroy a fresh grant. A headless OAuth owner commonly has to run two separate operations after a callback:
mcporter vault setwith the new token and DCR client information.refreshable_bearerconfig with that DCR client's ID and token endpoint.A concurrent probe between those operations can redeem the new refresh token with missing or previous client configuration. Providers that bind a refresh token to its DCR client return
invalid_grant; mcporter then correctly clears the rejectedtokensbut retainsclientInfo. The resulting vault entry looks like a successful registration with no token.Root cause
The two persistence paths handle
expires_indifferently.VaultPersistence.saveTokens()callsprepareStoredTokens(), whosewithStoredExpiry()addsexpires_at = now + expires_in.handleVaultSet()callssaveVaultEntry()directly withpayload.tokens, so no absolute expiry is recorded.shouldRefreshCachedToken()has no issue time for a relative-only token and returns true whenever bothexpires_inandrefresh_tokenare present.The behavior remains present in the published 0.13.7 package.
Reduced behavior proof
I configured one local
refreshable_bearerHTTP definition whose token endpoint only counted requests, then seeded this non-secret fixture through the real CLI:{ "tokens": { "access_token": "fixture-access-token", "token_type": "Bearer", "refresh_token": "fixture-refresh-token", "expires_in": 3600 }, "clientInfo": { "client_id": "fixture-client" } }The same proof on 0.13.7 also enters the relative-expiry refresh path. Adding the absolute field that mcporter's normal save path would have derived changes only this result:
No live provider or real credential was used.
Expected behavior
vault setshould persist imported tokens with the same expiry normalization asVaultPersistence.saveTokens(). A token seeded withexpires_in: 3600should not be refreshed until its actual expiry window.A focused regression could freeze time, call the vault-set path with relative expiry, read the stored entry, and assert the derived
expires_at. A runtime-level test can additionally assert that the first cached-token read performs zero token requests.