Skip to content

fix(auth): do not unlink provider token on egress auth_required signal#800

Merged
dcramer merged 4 commits into
mainfrom
fix/oauth-unlink-on-upstream-401
Jul 9, 2026
Merged

fix(auth): do not unlink provider token on egress auth_required signal#800
dcramer merged 4 commits into
mainfrom
fix/oauth-unlink-on-upstream-401

Conversation

@dcramer

@dcramer dcramer commented Jul 9, 2026

Copy link
Copy Markdown
Member

maybeHandleAuthSignal was unconditionally calling startAuthorizationPause with unlinkExistingProvider: true, deleting the stored OAuth token before sending a new auth link. When the signal originates from an upstream 401 after credential injection (scope/org/permission mismatch), the stored token is still valid — destroying it creates a re-auth loop where authorization succeeds in the browser but Junior immediately asks again.

Token preservation on auth_required

Removed unlinkExistingProvider: true from maybeHandleAuthSignal. The signal can represent either a pre-injection credential miss (missing/expired token) or a post-injection upstream 401. In the latter case the stored credential is fine. OAuth completion already overwrites the stored token via userTokenStore.set(), so proactive deletion is unnecessary in both cases.

ResumeTurnBusyError observability

Added logWarn when the OAuth callback waitUntil resume is dropped due to a busy conversation lock. Previously silently swallowed, making lock contention invisible in production.

Fixes #799

--

View Junior Session in Sentry

When maybeHandleAuthSignal receives an auth_required signal, it was
unconditionally calling startAuthorizationPause with
unlinkExistingProvider: true, deleting the stored OAuth token before
sending a new auth link.

This is wrong when the signal originates from an upstream 401 after
token injection (scope/org/permission mismatch): the stored token is
still valid, and deleting it creates a re-auth loop where every
authorization attempt succeeds in the browser but Junior immediately
asks again.

The fix: remove unlinkExistingProvider from maybeHandleAuthSignal.
OAuth completion already overwrites the stored token via
userTokenStore.set(), so proactive deletion is unnecessary. If the
token is genuinely revoked, re-auth will overwrite it on completion.

Also add a logWarn for ResumeTurnBusyError in the OAuth callback
waitUntil block — previously silently swallowed, making lock
contention invisible in production.

Fixes #799

Co-Authored-By: Claude Sonnet 4.5 <claude@anthropic.com>

Co-Authored-By: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
@vercel

vercel Bot commented Jul 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
junior-docs Ready Ready Preview, Comment Jul 9, 2026 1:22am

Request Review

dcramer and others added 2 commits July 9, 2026 01:03
…n context

Now that maybeHandleAuthSignal no longer passes unlinkExistingProvider,
the option and its block in startAuthorizationPause are dead code.
Remove the option, the unlink block, and the unlinkProvider import.

Also move conversation context (conversationId, actorId, channelId)
into the LogContext argument for oauth_callback_resume_busy so these
fields are indexed properly in structured logging.

Co-Authored-By: Claude Sonnet 4.5 <claude@anthropic.com>

Co-Authored-By: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Replace ternary-with-empty-object spread pattern with && spread in the
oauth_callback_resume_busy logWarn call. oxlint denies warnings, so
`...(cond ? {k:v} : {})` must be `...(cond && {k:v})`.

Co-Authored-By: Claude Sonnet 4.5 <claude@anthropic.com>

Co-Authored-By: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
After removing unlinkExistingProvider, the options object became a
single spread-only literal: { ...(scope ? {scope} : {}) }. oxlint
flags this as a useless new-object spread. Pass the options directly
as a conditional instead.

Co-Authored-By: Claude Sonnet 4.5 <claude@anthropic.com>

Co-Authored-By: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
@dcramer dcramer marked this pull request as ready for review July 9, 2026 01:25
@github-actions github-actions Bot added the risk: medium PR risk score: medium label Jul 9, 2026
@dcramer dcramer merged commit dbb1e08 into main Jul 9, 2026
20 checks passed
@dcramer dcramer deleted the fix/oauth-unlink-on-upstream-401 branch July 9, 2026 01:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: medium PR risk score: medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auth re-prompt loop after successful provider authorization

1 participant