You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Goal: let a caller recover from a dead session (the 8h wall, or a revoked / idle-expired
refresh token) without reconnecting, and optionally let the driver drive that recovery
automatically — separating re-auth policy (the user's) from mechanism (the driver's). Purely
reactive and daemonless, so it ships right after the PoC.
Scope (in):
Mechanism — holder.reauthenticate(timeout) (surfaced as Connection.reauthenticate()):
synchronous, routed through the same single-flight gate as refresh, it re-runs login()
(transient callback server + browser + code exchange) and swaps in a fresh TokenSet, resetting
the 8h clock. Process-wide, so one re-auth re-arms every Connection and N concurrent callers at
the wall collapse to one browser bounce.
Policy knob — a connect()-time reauth= selector over the ReauthenticationRequired
exception (a typed OperationalError subclass, already raised by child 3):
"raise" (default) — auth_flow raises; the app catches it (anywhere on the DBAPI surface)
and either calls connection.reauthenticate() then retries, or reconnects. The driver never
pops a browser unbidden — the settled "don't ambush a live query" rule.
reauth=<callback> — the user's registered "begin browser re-auth" intent: on the wall, the
first thread through the gate invokes on_reauth_required(ctx), which calls ctx.reauthenticate() to consent + drive (blocking), or returns / raises to decline. ctx
carries the reason (idle / absolute / revoked) + org so the callback can pre-notify a human,
log, or marshal to a UI thread — but not the authorize_url / code plumbing, which stays
the driver's.
Out of scope:proactive re-auth ahead of the wall (child 8 — needs the daemon); the daemon
itself (child 7). Even here re-auth still raises on timeout / cancel / failure — the callback
only demotes the exception from default outcome to last resort.
Prior art:mcp-confluent — OAuthHolder.ensureLoggedIn() already re-logs in on demand once refreshTokenExpired() / hasNonTransientError(), which is exactly our reactive path; we add the
explicit reauthenticate() entry point and the reauth= policy over it. ide-sidecar — signals
re-auth-needed (connection → NONE / FAILED) and leaves the client to restart sign-in: the
"raise and let the app decide" default, without an in-place recovery call.
Sized right: one holder method reusing login(), the reauth= validation, and a typed
exception; no daemon, and no new async surface — the browser round-trip is already behind wait_for_code.
refresh token) without reconnecting, and optionally let the driver drive that recovery
automatically — separating re-auth policy (the user's) from mechanism (the driver's). Purely
reactive and daemonless, so it ships right after the PoC.
holder.reauthenticate(timeout)(surfaced asConnection.reauthenticate()):synchronous, routed through the same single-flight gate as refresh, it re-runs
login()(transient callback server + browser + code exchange) and swaps in a fresh
TokenSet, resettingthe 8h clock. Process-wide, so one re-auth re-arms every Connection and N concurrent callers at
the wall collapse to one browser bounce.
connect()-timereauth=selector over theReauthenticationRequiredexception (a typed
OperationalErrorsubclass, already raised by child 3):"raise"(default) —auth_flowraises; the app catches it (anywhere on the DBAPI surface)and either calls
connection.reauthenticate()then retries, or reconnects. The driver neverpops a browser unbidden — the settled "don't ambush a live query" rule.
reauth=<callback>— the user's registered "begin browser re-auth" intent: on the wall, thefirst thread through the gate invokes
on_reauth_required(ctx), which callsctx.reauthenticate()to consent + drive (blocking), or returns / raises to decline.ctxcarries the reason (idle / absolute / revoked) + org so the callback can pre-notify a human,
log, or marshal to a UI thread — but not the
authorize_url/ code plumbing, which staysthe driver's.
itself (child 7). Even here re-auth still raises on timeout / cancel / failure — the callback
only demotes the exception from default outcome to last resort.
CCloudOAuthprovider: login + on-request refresh + two auth views #153, Process-wide holder: one OAuth identity per process #154,connect()wiring for interactive OAuth #155 (thelogin()mechanism + single-flight gate, the holder, and theconnect()surface the knob rides on).OAuthHolder.ensureLoggedIn()already re-logs in on demand oncerefreshTokenExpired()/hasNonTransientError(), which is exactly our reactive path; we add theexplicit
reauthenticate()entry point and thereauth=policy over it. ide-sidecar — signalsre-auth-needed (connection →
NONE/FAILED) and leaves the client to restart sign-in: the"raise and let the app decide" default, without an in-place recovery call.
login(), thereauth=validation, and a typedexception; no daemon, and no new async surface — the browser round-trip is already behind
wait_for_code.