Repository navigation
Refresh WorkOS token during kcap setup provisioning poll - #259
Conversation
The create-a-tenant flow provisions then polls /api/signup/status for up to ~10 minutes, but it reused the single org-less WorkOS access token for the whole wait. WorkOS AuthKit access tokens live only ~5 minutes, so it expired mid-poll; the server then 401'd every status check, which GetStatusAsync swallowed to a null "still provisioning", and the CLI spun silently until timeout even though the tenant had gone live. - WorkOSTokenSource: refreshes the access token (public-client refresh_token grant, OAuthLoginFlow.RefreshWorkOSTokenAsync) before it lapses; every provision/poll call pulls a fresh token. WorkOS rotates refresh tokens single-use, so the final org-switch now uses WorkOSTokenSource.CurrentRefreshToken, not the login-time one. - GetStatusAsync surfaces the HTTP status (StatusOutcome); ProvisioningPoll.Classify turns each poll result into a verdict so 403/404/failed/active-without-org end with a clear message instead of an infinite silent spin, and the spinner shows liveness. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PR Summary by QodoRefresh WorkOS tokens during tenant provisioning poll in
AI Description
Diagram
High-Level Assessment
Files changed (11)
|
Code Review by Qodo
Context used 1.
|
The refresh runs automatically and repeatedly during the ~10-minute provisioning poll, so a network/timeout/JSON blip in the refresh must not throw and abort the whole flow (it contradicted WorkOSTokenSource.GetAsync's own "degrades to the existing token" contract). - WorkOSTokenSource.GetAsync now catches transient exceptions from the refresh delegate and degrades to the current token; a genuine ct cancellation still propagates (!ct.IsCancellationRequested). - RefreshWorkOSTokenAsync wraps its HTTP + JSON in the same swallow-and-degrade guard as TenantProvisioningClient, returning null on transport/parse failure. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Problem
During
kcap setup(WorkOS org SSO, no existing tenant) the CLI offers to create a tenant, provisions it, then pollsGET /api/signup/statusfor it to go live. The poll never completes even after the tenant is provisioned and live — the spinner sits at "Provisioning {slug}.kcap.ai…" until it times out (~10 min) and no profile is saved.Root cause
The poll reused a single org-less WorkOS access token for the entire ~10-minute wait, but WorkOS AuthKit access tokens live only ~5 minutes. Once it expired, the signup server returned 401 for every status check, and
GetStatusAsyncswallowed any non-2xx to a null "still provisioning" — so the CLI spun silently until timeout even though the tenant had gone live.Same poll, contributing defects:
GetStatusAsynccollapsed 401/403/404/transport-error all tonull(zero diagnostics), and anactiverow withoutworkosOrgId(which the server contract explicitly allows) was treated as not-done — another infinite-spin path.Fix
WorkOSTokenSource— refreshes the access token (public-clientrefresh_tokengrant,OAuthLoginFlow.RefreshWorkOSTokenAsync) before it lapses; every provision/poll call pulls a fresh token. WorkOS rotates refresh tokens single-use, so the final org-switch now usesWorkOSTokenSource.CurrentRefreshToken, not the login-time value (otherwise the switch would 401 after a long poll).GetStatusAsync→StatusOutcomesurfaces the HTTP status;ProvisioningPoll.Classifyturns each poll result into a verdict so 403/404/failed/active-without-org end with a clear message instead of a silent spin, and the spinner shows liveness per attempt.Tests
WorkOSTokenSourceTests(6),ProvisioningPollTests(11),GetStatusAsyncstatus-surfacing (TenantProvisioningClientTests), and aWorkOSDiscoveryTestsguard proving the rotated refresh token is used for the org-switch. All written test-first (watched fail).dotnet publish -c Releaseclean — no IL3050/IL2026 AOT warnings.No README change: internal bugfix, no user-facing CLI surface change (no new/renamed command, flag, default, or prerequisite).
Closes #258
AI-1171
🤖 Generated with Claude Code