(
row: ConnectionRow,
provider: CredentialProvider,
trigger: RefreshTrigger,
)
| 2422 | |
| 2423 | // Perform the actual refresh-token grant and persist the rotated material. |
| 2424 | const performTokenRefresh = ( |
| 2425 | row: ConnectionRow, |
| 2426 | provider: CredentialProvider, |
| 2427 | trigger: RefreshTrigger, |
| 2428 | ): Effect.Effect<string | null, StorageFailure | CredentialResolutionError> => |
| 2429 | Effect.gen(function* () { |
| 2430 | const owner = row.owner as Owner; |
| 2431 | const reauth = ( |
| 2432 | message: string, |
| 2433 | options?: { readonly credentialMissing?: boolean; readonly blockedByAdmin?: boolean }, |
| 2434 | ): CredentialResolutionError => |
| 2435 | new CredentialResolutionError({ |
| 2436 | owner, |
| 2437 | integration: IntegrationSlug.make(row.integration), |
| 2438 | name: ConnectionName.make(row.name), |
| 2439 | message, |
| 2440 | reauthRequired: true, |
| 2441 | ...(options?.credentialMissing === true ? { credentialMissing: true } : {}), |
| 2442 | ...(options?.blockedByAdmin === true ? { blockedByAdmin: true } : {}), |
| 2443 | }); |
| 2444 | |
| 2445 | // A recorded invalid_grant is the AS's standing verdict on this grant: |
| 2446 | // re-sending it cannot succeed, so don't. Fail as reauth-required |
| 2447 | // without a token request — the reconnect mint rewrites |
| 2448 | // `provider_state` and thereby re-arms refresh. Without this gate a |
| 2449 | // dead connection re-sent its dead grant on every proactive cycle, |
| 2450 | // indefinitely (owner.com's Datadog connections: 100+ identical |
| 2451 | // rejections over two days, surfacing nothing). |
| 2452 | const reauthState = oauthReauthRequiredFromProviderState(row.provider_state); |
| 2453 | if (reauthState !== null) { |
| 2454 | yield* Effect.annotateCurrentSpan({ "executor.oauth.refresh.skipped_known_dead": true }); |
| 2455 | const recordedHealth = Option.getOrNull(decodeLastHealth(row.last_health)); |
| 2456 | const recordedDetail = |
| 2457 | reauthState.oauthReauthRequiredDetail ?? |
| 2458 | (recordedHealth?.status === "expired" ? recordedHealth.detail : undefined); |
| 2459 | const detail = |
| 2460 | recordedDetail === undefined |
| 2461 | ? "The authorization server rejected this connection's refresh token (invalid_grant). Reconnect to continue." |
| 2462 | : recordedDetail.endsWith("Reconnect to continue.") |
| 2463 | ? recordedDetail |
| 2464 | : `${recordedDetail} Reconnect to continue.`; |
| 2465 | // An admin-policy denial recorded on the dead grant must survive |
| 2466 | // reconstruction: without the flag, callers past the first denial |
| 2467 | // would show ordinary reconnect guidance — the interactive OAuth |
| 2468 | // route the policy contract forbids offering. |
| 2469 | return yield* reauth(detail, { |
| 2470 | blockedByAdmin: recordedDeadGrantReason(reauthState) === "blocked_by_admin", |
| 2471 | }); |
| 2472 | } |
| 2473 | |
| 2474 | // Load the backing app. A `first-party:` slug resolves from host config |
| 2475 | // (deployment-owned identity, in-memory secret); a stored slug loads by |
| 2476 | // the owner STORED on the connection (a Personal connection may be |
| 2477 | // backed by a shared Workspace app) — no derivation — with its secret |
| 2478 | // resolved out of the credential provider by item id. |
| 2479 | const clientSlug = String(row.oauth_client); |
| 2480 | const clientRow: RefreshClient | null = yield* Effect.gen(function* () { |
| 2481 | if (isFirstPartyOAuthClientSlug(clientSlug)) { |
no test coverage detected