Skip to content

feat(integrations): Add support for federated auth integrations, starting with BigQuery#399

Merged
tkislan merged 32 commits into
mainfrom
tk/integration-federated-auth-big-query
May 27, 2026
Merged

feat(integrations): Add support for federated auth integrations, starting with BigQuery#399
tkislan merged 32 commits into
mainfrom
tk/integration-federated-auth-big-query

Conversation

@tkislan
Copy link
Copy Markdown
Contributor

@tkislan tkislan commented May 22, 2026

Summary by CodeRabbit

  • New Features

    • Google OAuth for BigQuery integrations with Authenticate/Re-authenticate actions and per-integration token status in the integrations panel.
    • BigQuery SQL blocks may run using stored OAuth tokens (queries execute with refreshed access tokens).
    • Kernels automatically restart when relevant integration tokens change; orphaned tokens are pruned.
    • BigQuery form supports both service-account and OAuth modes; Deepnote domain now resolves per-resource.
  • Bug Fixes

    • Web shows a clear “not supported in web” message for federated auth flows.
  • Tests

    • Extensive unit coverage for federated auth flows, token storage, OAuth loopback, codegen, and related helpers.

Review Change Stack

tkislan and others added 7 commits May 21, 2026 17:43
Lay the foundation for BigQuery Google OAuth integration: DI symbols,
shared type contracts, command/localization keys, and vendored
@deepnote/blocks helpers required to generate federated SQL cell code
without persisting access tokens.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Boot the federated-auth runtime: encrypted refresh-token storage with
metadata-fingerprint stale detection, a Google OAuth strategy that owns
its own verify callback, a session-less PKCE store, and a loopback
flow that mints a refresh token via the user's browser. No access
token is persisted — refresh is per-execution.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…xecution (M3)

Each federated SQL cell now runs through a code generator that mints
a fresh access token from Google before execution, defines it in a
kernel-side variable via a silent prelude (store_history disabled),
and references that variable from the cell's main Python — so the
access token never lands in Jupyter's In[] history. Plumbs the
generator through KernelProvider → NotebookKernelExecution →
CellExecutionFactory → CellExecution as an optional dep so the
web build degrades gracefully.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…d (M4)

Wire the federated auth flow into reachable surfaces: a "Google OAuth"
option on the BigQuery integration form, an Authenticate button with
status pill in the integration list, a command that drives the loopback
flow on desktop (and a clear "not supported" toast on web/remote), and
all the localization plumbing. Stale tokens are cleared automatically
when OAuth metadata changes, and federated integrations skip the
kernel-startup env-var batch so the per-cell refresh path owns
credential delivery.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add the two activation services that keep federated-auth state coherent
across the extension host: a bridge that restarts kernels when a token
changes (so the next cell starts with a clean os.environ) and an orphan
cleaner that drops stored tokens for integrations that no longer exist.
Also distinguish the misconfigured-OAuth-client error from the
not-authenticated error in cell execution, so the user gets a clear
"verify your credentials in integration settings" message rather than
a generic re-auth prompt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The federated-auth code generator persists rotated refresh tokens during
cell preparation. That save was firing onDidChangeTokens, which the
kernel-restart bridge handled by queuing a kernel.restart() — landing
microseconds later while the very SQL cell that triggered the rotation
was still executing.

The new refresh token doesn't change anything the kernel sees: the
extension mints a fresh access token via the per-cell pre-execute on
every SQL block run, so there's no stale state to clear. Add a
{ silent: true } option to FederatedAuthTokenStorage.save() and use it
on the rotation path. Initial-auth and metadata-change saves keep
firing the event as before.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 22, 2026

📝 Walkthrough

Walkthrough

This PR implements federated authentication for BigQuery integrations in the VS Code Deepnote extension. It introduces OAuth-based token management with refresh logic, a loopback browser flow for acquiring credentials, SQL code generation that embeds fresh access tokens, and extension services that manage token lifecycle. The webview now displays token status and supports re-authentication. Cell execution conditionally runs a silent prelude to set connection context before executing federated SQL blocks. Kernel restarts are triggered when tokens change, and stale tokens are pruned when integrations are removed.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

@codecov
Copy link
Copy Markdown

codecov Bot commented May 22, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 0%. Comparing base (862f6e9) to head (165b7e5).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@     Coverage Diff     @@
##   main   #399   +/-   ##
===========================
===========================
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/kernels/execution/cellExecution.federatedAuth.unit.test.ts`:
- Around line 217-225: Replace the multiple individual property assertions on
the first call with a single assert.deepStrictEqual comparing the full request
payload: build an expected object for the prelude (e.g., { code: prelude,
silent: true, store_history: false, allow_stdin: false, stop_on_error: true })
and call assert.deepStrictEqual(preludeContent, expectedPrelude); keep the
existing boolean check for preludeDispose if desired or include it in a second
deepStrictEqual against the full args tuple; do the same refactor for the second
call (calls[1] / its requestArgs/requestContent) referenced in the comment so
each payload comparison uses a single assert.deepStrictEqual rather than many
assert.strictEqual calls.

In `@src/kernels/execution/cellExecution.ts`:
- Around line 486-500: The current await of
kernelConnection.requestExecute(...).done ignores execute_reply content.status
so errors inside the federated prelude don't throw; change the call to capture
the reply (e.g., const reply = await
kernelConnection.requestExecute({...}).done), then inspect reply.content?.status
and if it equals "error" call logger.error with context including
federated.prelude and the reply content, and return
this.completedWithErrors(reply) (or an Error created from the reply) to stop
main execution; keep existing try/catch for transport exceptions around the same
kernelConnection.requestExecute call and use federated.prelude,
kernelConnection.requestExecute, this.completedWithErrors, and logger.error to
locate where to implement the check.

In
`@src/notebooks/deepnote/integrations/federatedAuth/federatedAuthInvariants.unit.test.ts`:
- Around line 10-13: The current conditional type distributes over the union
'accessToken' | 'expiresAt' causing a false negative; update the invariant to
test the union as a whole by using a non-distributive or aggregate check (for
example, use Extract<keyof FederatedAuthTokenEntry, Forbidden> extends never ?
true : never or wrap Forbidden in a tuple like [Forbidden] extends [keyof
FederatedAuthTokenEntry] ?) so that the test fails if any forbidden key is
present; change the AssertEntryOmitsForbidden definition that references
FederatedAuthTokenEntry and Forbidden accordingly and keep the rest of the
assertion (_entryShapeCheck and void usage) intact.

In
`@src/notebooks/deepnote/integrations/federatedAuth/federatedAuthKernelRestartBridge.node.unit.test.ts`:
- Line 83: Replace the repeated magic-number waits by extracting a named
constant and helper: add a top-of-module constant (e.g., const TEST_WAIT_MS =
10) and a small async helper function (e.g., async function waitMs(ms =
TEST_WAIT_MS) { return new Promise(resolve => setTimeout(resolve, ms)); }) and
then replace every occurrence of await new Promise((resolve) =>
setTimeout(resolve, 10)); in the tests with await waitMs(); so fixtures and
tests (references in federatedAuthKernelRestartBridge.node.unit.test.ts) use the
shared constant and helper to avoid duplication.

In
`@src/notebooks/deepnote/integrations/federatedAuth/federatedAuthOrphanedTokenCleaner.node.ts`:
- Around line 31-33: In FederatedAuthOrphanedTokenCleaner, the activate() method
is currently a no-op so any orphaned tokens present at startup are never
cleaned; update activate() to invoke the existing orphaned-token cleanup routine
used for integration-change events (call the exact method name in this class —
e.g., cleanOrphanedTokens(), runOrphanedTokenCleanup(), or similar) so cleanup
runs once on activation, await or handle the returned Promise if the cleanup is
async, and catch/log any errors so activation doesn't crash.

In `@src/notebooks/deepnote/integrations/integrationWebview.ts`:
- Around line 665-666: When deleting an integration, token cleanup
(tokenStorage.delete(integrationId)) must not be allowed to abort the overall
delete/reset flow if it fails; wrap the tokenStorage.delete(integrationId) calls
(the ones at/around where integrationStorage.delete(...) is called in
integrationWebview.ts) in a try/catch, log the error (or call process/project
logger) inside the catch, and do not rethrow so the in-memory and project state
transitions (the integrationStorage.delete and subsequent state updates) always
complete; apply the same pattern to both occurrences (the calls at the two
locations noted).
- Around line 50-52: The event callback passed to tokenStorage.onDidChangeTokens
calls this.updateWebview() without handling rejections, which can produce
unhandled promise rejections; change the callback so it consumes errors from
updateWebview (for example replace void this.updateWebview() with
this.updateWebview().catch(err => { /* handle or log the error */ }) or wrap the
call in an async IIFE with try/catch) so any rejection from updateWebview is
caught; reference tokenStorage.onDidChangeTokens and the updateWebview method
when making the change.

In
`@src/platform/notebooks/deepnote/sqlIntegrationEnvironmentVariablesProvider.ts`:
- Around line 110-116: The Promise.all call loading integration configs will
abort all work if any getIntegrationConfig rejects; change to per-item error
isolation by using Promise.allSettled over projectIntegrations.map(...) (or wrap
each await in try/catch inside the map) to collect only fulfilled results into
allConfigs, filter out rejected/undefined entries, and log the individual errors
(including integration.id) so one failing getIntegrationConfig call in
integrationStorage.getIntegrationConfig does not prevent returning other env
vars (including DuckDB).

In `@src/webviews/webview-side/integrations/BigQueryForm.tsx`:
- Around line 44-55: buildInitialConfig currently returns a blind clone of
existingConfig which can carry an unsupported authMethod into the form; change
it to clone the object (structuredClone(existingConfig)) then validate
existingConfig.authMethod against the supported set (e.g., 'service-account' and
'google-oauth') and if it's not supported, normalize it to a safe default (for
example 'service-account') and clear or reset any auth-specific fields (e.g.,
serviceAccountKey, oauthToken, oauthClientId/secrets) so the form doesn't start
in an invalid state; use the function buildInitialConfig and
createEmptyBigQueryConfig as anchors for where to perform this normalization.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 36bbea8e-d36e-4a6d-b50b-3de8abb8e3e5

📥 Commits

Reviewing files that changed from the base of the PR and between 8efe0ed and 4da300a.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (41)
  • package.json
  • package.nls.json
  • src/kernels/execution/cellExecution.federatedAuth.unit.test.ts
  • src/kernels/execution/cellExecution.ts
  • src/kernels/kernelExecution.ts
  • src/kernels/kernelProvider.node.ts
  • src/messageTypes.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthCommandHandler.node.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthCommandHandler.node.unit.test.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthCommandHandler.web.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthCommandHandler.web.unit.test.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthInvariants.unit.test.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthKernelRestartBridge.node.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthKernelRestartBridge.node.unit.test.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthOrphanedTokenCleaner.node.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthOrphanedTokenCleaner.node.unit.test.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthSqlBlockCodeGenerator.node.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthSqlBlockCodeGenerator.node.unit.test.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthTokenStorage.node.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthTokenStorage.node.unit.test.ts
  • src/notebooks/deepnote/integrations/federatedAuth/googleOAuthProvider.node.ts
  • src/notebooks/deepnote/integrations/federatedAuth/googleOAuthProvider.node.unit.test.ts
  • src/notebooks/deepnote/integrations/federatedAuth/oauthLoopbackFlow.node.ts
  • src/notebooks/deepnote/integrations/federatedAuth/oauthLoopbackFlow.node.unit.test.ts
  • src/notebooks/deepnote/integrations/federatedAuth/vendoredBlocksHelpers.ts
  • src/notebooks/deepnote/integrations/federatedAuth/vendoredBlocksHelpers.unit.test.ts
  • src/notebooks/deepnote/integrations/integrationWebview.ts
  • src/notebooks/deepnote/integrations/integrationWebview.unit.test.ts
  • src/notebooks/deepnote/integrations/types.ts
  • src/notebooks/serviceRegistry.node.ts
  • src/notebooks/serviceRegistry.web.ts
  • src/platform/common/constants.ts
  • src/platform/common/utils/localize.ts
  • src/platform/notebooks/deepnote/integrationTypes.ts
  • src/platform/notebooks/deepnote/sqlIntegrationEnvironmentVariablesProvider.ts
  • src/platform/notebooks/deepnote/sqlIntegrationEnvironmentVariablesProvider.unit.test.ts
  • src/webviews/webview-side/integrations/BigQueryForm.tsx
  • src/webviews/webview-side/integrations/IntegrationItem.tsx
  • src/webviews/webview-side/integrations/IntegrationList.tsx
  • src/webviews/webview-side/integrations/IntegrationPanel.tsx
  • src/webviews/webview-side/integrations/types.ts

Comment thread src/kernels/execution/cellExecution.federatedAuth.unit.test.ts Outdated
Comment thread src/kernels/execution/cellExecution.ts Outdated
Comment thread src/notebooks/deepnote/integrations/integrationWebview.ts
Comment thread src/notebooks/deepnote/integrations/integrationWebview.ts Outdated
Comment thread src/platform/notebooks/deepnote/sqlIntegrationEnvironmentVariablesProvider.ts Outdated
Comment thread src/webviews/webview-side/integrations/BigQueryForm.tsx
tkislan and others added 19 commits May 22, 2026 11:32
The previous flow opened Google's `/authorize` directly from the
extension's loopback URL, which works only for Desktop-app Google
clients. Customers using a Web-application client (so the same client
can be shared with the deepnote.com web product) can't register
`http://127.0.0.1:<random-port>` redirect URIs, so the loopback hit
`redirect_uri_mismatch`.

Switch to a delegated flow:
- Extension binds a random loopback port, externalizes it with
  `env.asExternalUri` (so SSH-remote / WSL / dev-container work), and
  opens `https://<deepnote.domain>/auth/bigquery/extension/start` with
  the loopback URL as `finalRedirect`.
- deepnote.com performs the OAuth handshake using its already-registered
  `/auth/bigquery/google-oauth-callback` as Google's `redirect_uri`,
  then 302s the browser back to the loopback URL with the authorization
  `code` attached (or `error`+`error_description` on consent denial).
- The extension exchanges the code directly with Google's `/token`,
  keeping the refresh token out of deepnote.com entirely. PKCE +
  state-nonce protect against code interception in transit.

Removes the passport-google-oauth20 dependency from this path; the
loopback server now exposes only `/auth/callback`. Token exchange is
done with a new `exchangeAuthorizationCode` helper that mirrors the
existing `fetchFreshAccessToken` shape and error taxonomy.

Multi-window safety is automatic — each window's random port is the
disambiguator, so the browser's redirect lands in exactly one window.

`deepnote.domain` is now a `resource`-scoped setting so different
workspace folders / notebook files can point at different hosts; the
import client and the OAuth start URL both pass the active resource
URI to the configuration lookup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lementations

Updated unit tests for FederatedAuth components to utilize new fake storage implementations for integration and token storage. This change simplifies the test setup and enhances clarity by removing unnecessary complexity related to manual state management. The tests now leverage the `createFakeIntegrationStorage` and `createFakeTokenStorage` functions, ensuring a more consistent and maintainable testing environment.
`@jupyterlab/services` resolves `IShellFuture.done` with the reply
unconditionally and only rejects on transport/disposal errors. The
federated prelude's `.done` was awaited but the reply was discarded, so
a kernel that returned `content.status === 'error'` silently proceeded
to the main execute against an unset `__deepnote_federated_sql_connection__*`
global, surfacing a confusing `NameError` instead of the real cause.

Capture the reply, check `content.status === 'error'`, and surface it
as a `KernelError` via `completedWithErrors`. Add a unit test that
resolves the prelude with an error reply and verifies the main execute
is not called.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The form told users to create a 'Desktop app' OAuth client, but the
federated-auth flow sends redirect_uri=https://deepnote.com/auth/bigquery/
google-oauth-callback to Google. That HTTPS redirect is only valid on a
'Web application' OAuth client; Desktop clients restrict redirects to
localhost/custom-scheme. Users following the previous help would create
the wrong client type and hit redirect_uri_mismatch on first authorize.

Update both the l10n string and the in-form fallback to instruct users
to create a 'Web application' client and add the deepnote.com callback
to its authorized redirect URIs. Add a source-grep regression test that
fails if either copy reverts to the old wording or drops the callback URL.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The test just read the form and localize.ts off disk and asserted the
string contents — it didn't render the component, exercise any prop
flow, or catch any behavior that a typo wouldn't catch. Locking in
constants via source-grep is noise, not coverage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rams

The deepnote.com OAuth proxy start URL was emitting camelCase query
parameters (clientId, codeChallenge, finalRedirect). OAuth 2.0 (RFC 6749)
uses snake_case throughout (client_id, code_challenge, etc.), and the
loopback callback already receives Google's snake_case (code, state,
error_description), so the inbound and outbound wire formats should match.

Only the URL keys change; the TypeScript params on buildExtensionStartUrl
stay camelCase (TS convention). Tests updated to assert the snake_case
wire format.

Note: the deepnote.com server-side handler reading these params must be
updated to match before this lands in production.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Updated the `IFederatedAuthSqlBlockCodeGenerator` interface to return a single string containing the connection JSON instead of an object with separate prelude and cellCode properties. Adjusted the `FederatedAuthSqlBlockCodeGenerator` implementation and related tests to reflect this change, ensuring that the access token is embedded directly in the execute call. This streamlines the code generation process for federated SQL blocks and enhances clarity in the execution flow.
The federated-auth OAuth flow was switched to a deepnote.com-proxied
handshake in a2c8381, so the extension no longer drives Google's
`/authorize` itself — the loopback server only reads `code`/`state`
from a single callback URL and exchanges it via `fetch`. `passport` /
`passport-google-oauth20` (+ their `@types`) are no longer imported
anywhere; remove them from package.json and the lockfile.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… cell execution

- Added a new error class `CellExecutionOutputError` to handle cases where an error message has already been written to the cell output.
- Updated the `completedWithErrors` method in `CellExecution` to accept an options object for error handling, allowing for better control over how errors are written to cell output (append or replace).
- Modified tests to assert the correct error type is caught and handled.
- Enhanced error handling in `VSCodeNotebookController` to prevent duplicate error messages from being logged when a `CellExecutionOutputError` is encountered.
@tkislan
Copy link
Copy Markdown
Contributor Author

tkislan commented May 27, 2026

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 27, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/webviews/webview-side/integrations/IntegrationItem.tsx (1)

8-14: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Keep props alphabetically ordered after adding onAuthenticate.

onAuthenticate was appended, but this interface/destructuring block should stay alphabetized to match repo ordering rules.

Proposed reorder
 export interface IIntegrationItemProps {
     integration: IntegrationWithStatus;
+    onAuthenticate: (integrationId: string) => void;
     onConfigure: (integrationId: string) => void;
-    onReset: (integrationId: string) => void;
     onDelete: (integrationId: string) => void;
-    onAuthenticate: (integrationId: string) => void;
+    onReset: (integrationId: string) => void;
 }

 export const IntegrationItem: React.FC<IIntegrationItemProps> = ({
     integration,
+    onAuthenticate,
     onConfigure,
-    onReset,
     onDelete,
-    onAuthenticate
+    onReset
 }) => {

As per coding guidelines: "Order method, fields and properties, first by accessibility and then by alphabetical order".

Also applies to: 57-63

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/webviews/webview-side/integrations/IntegrationItem.tsx` around lines 8 -
14, The IIntegrationItemProps interface and the props destructuring in the
IntegrationItem component must be alphabetized: move onAuthenticate into the
correct alphabetical position among integration, onConfigure, onDelete, onReset,
etc., and reorder the corresponding destructured props in the IntegrationItem
function to match; update both the IIntegrationItemProps declaration and the
destructuring usage so property order is alphabetical (e.g., integration,
onAuthenticate, onConfigure, onDelete, onReset).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/kernels/errors/cellExecutionOutputError.ts`:
- Around line 1-2: This new file contains the Microsoft copyright header at the
top; remove the header lines so the file only contains the TypeScript code
(i.e., delete the leading comment block in
src/kernels/errors/cellExecutionOutputError.ts), leaving the existing exported
symbol(s) like CellExecutionOutputError and any imports/exports intact.

In `@src/kernels/execution/cellExecution.federatedAuth.unit.test.ts`:
- Around line 102-106: Test setup creates a CancellationTokenSource named
tokenSource and pushes it to disposables but it is never used or disposed;
either remove the unused tokenSource and its push from the setup block, or use
it in the tests (e.g., pass tokenSource.token to functions under test and call
tokenSource.cancel() in a test) and ensure you call tokenSource.dispose() (or
remove from disposables cleanup if you keep it) so CancellationTokenSource is
actually exercised and properly disposed; locate the tokenSource variable in the
setup function and update either its creation/push or add usage and disposal
accordingly.

In `@src/messageTypes.ts`:
- Around line 231-250: The block of new LocalizedMessages keys for BigQuery
federated-auth is not alphabetized; reorder the keys within the BigQuery
federated-auth section so they follow alphabetical order by property name (e.g.,
place integrationsAuthenticate, integrationsAuthenticating,
integrationsAuthenticationFailed, integrationsAuthenticationSucceeded,
integrationsBigQueryAuthMethodGoogleOauth, integrationsBigQueryAuthMethodLabel,
integrationsBigQueryAuthMethodServiceAccount, integrationsBigQueryClientIdLabel,
integrationsBigQueryClientIdPlaceholder, integrationsBigQueryClientSecretLabel,
integrationsBigQueryClientSecretPlaceholder,
integrationsBigQueryNotAuthenticated, integrationsBigQueryProjectLabel,
integrationsBigQueryProjectPlaceholder,
integrationsFederatedAuthNotSupportedInWeb, integrationsReauthenticate,
integrationsTokenStatusAuthenticated, integrationsTokenStatusDisconnected);
update the sequence around the identifiers mentioned
(integrationsBigQueryAuthMethodLabel,
integrationsBigQueryAuthMethodServiceAccount,
integrationsBigQueryAuthMethodGoogleOauth, integrationsBigQueryProjectLabel,
integrationsBigQueryProjectPlaceholder, integrationsBigQueryClientIdLabel,
integrationsBigQueryClientIdPlaceholder, integrationsBigQueryClientSecretLabel,
integrationsBigQueryClientSecretPlaceholder, integrationsAuthenticate,
integrationsReauthenticate, integrationsTokenStatusAuthenticated,
integrationsTokenStatusDisconnected, integrationsAuthenticating,
integrationsAuthenticationSucceeded, integrationsAuthenticationFailed,
integrationsBigQueryNotAuthenticated,
integrationsFederatedAuthNotSupportedInWeb) so the section follows alphabetical
ordering.

In `@src/notebooks/controllers/vscodeNotebookController.ts`:
- Around line 670-674: The catch blocks currently reassign the catch parameter
ex using WrappedError.unwrap(ex), which violates noCatchAssign; instead create a
new local (e.g., const unwrapped = WrappedError.unwrap(ex)) and use that for
subsequent instanceof checks and logging/rendering (replace uses of the
reassigned ex with unwrapped in the block where you check for
CellExecutionOutputError and any error rendering code). Ensure you do this for
all catch blocks in vscodeNotebookController.ts that call WrappedError.unwrap on
the catch parameter.

In
`@src/notebooks/deepnote/integrations/federatedAuth/federatedAuthTestHelpers.ts`:
- Around line 128-130: Introduce a top-of-module named constant (e.g.,
SETTLE_ASYNC_HANDLERS_DELAY_MS) and use it as the default timeout for
settleAsyncHandlers instead of the magic number 10; update the
settleAsyncHandlers function signature to default ms to that constant and
replace any inline uses of 10 for this helper with the new constant so the
intent and configuration are centralized.

In `@src/notebooks/deepnote/integrations/federatedAuth/vendoredBlocksHelpers.ts`:
- Around line 32-34: The escapePythonString function fails to escape carriage
returns; update escapePythonString to also replace '\r' with '\\r' (ensuring you
still first escape backslashes, then single quotes, then newlines and carriage
returns) so generated Python string literals won't break when value contains
'\r'.

In `@src/notebooks/deepnote/integrations/integrationWebview.ts`:
- Around line 36-57: The tokenStorageDisposables array (used to hold the
onDidChangeTokens subscription) is never disposed, causing a leak; add a cleanup
path by implementing a dispose() on the IntegrationWebviewProvider (or register
the provider with IDisposableRegistry) that iterates tokenStorageDisposables and
calls dispose() on each Disposable (and clears the array), and ensure
this.dispose() is invoked when the provider is torn down so the
IFederatedAuthTokenStorage.onDidChangeTokens subscription is unsubscribed.
- Around line 489-521: The tokenStorage.delete calls inside
invalidateStaleFederatedToken can throw and bubble up, breaking the caller's
saveConfiguration; wrap both delete invocations in a safe failure handler
(either a try/catch around the await this.tokenStorage.delete(integrationId)
calls or append .catch(err => { logger.warn(...); })) so deletion errors are
logged but do not reject the surrounding operation; adjust logging to include
the error and continue without rethrowing to ensure saveConfiguration can
proceed.

In
`@src/platform/notebooks/deepnote/sqlIntegrationEnvironmentVariablesProvider.ts`:
- Around line 14-20: Reorder imports so third-party package imports come before
local project imports: move the block importing DatabaseIntegrationConfig,
FederatedAuthMethod, getEnvironmentVariablesForIntegrations, and
isFederatedAuthMethod from '`@deepnote/database-integrations`' above the local
imports (the '../../common/*' and './*' imports) in
sqlIntegrationEnvironmentVariablesProvider (locate the import statements by
those symbol names) to follow the third-party/local grouping guideline.

---

Outside diff comments:
In `@src/webviews/webview-side/integrations/IntegrationItem.tsx`:
- Around line 8-14: The IIntegrationItemProps interface and the props
destructuring in the IntegrationItem component must be alphabetized: move
onAuthenticate into the correct alphabetical position among integration,
onConfigure, onDelete, onReset, etc., and reorder the corresponding destructured
props in the IntegrationItem function to match; update both the
IIntegrationItemProps declaration and the destructuring usage so property order
is alphabetical (e.g., integration, onAuthenticate, onConfigure, onDelete,
onReset).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e109f31b-1a4a-4bbb-9158-99fd3e0b3815

📥 Commits

Reviewing files that changed from the base of the PR and between cf5d53f and cdd519b.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (49)
  • cspell.json
  • package.json
  • package.nls.json
  • src/kernels/errors/cellExecutionOutputError.ts
  • src/kernels/execution/cellExecution.federatedAuth.unit.test.ts
  • src/kernels/execution/cellExecution.ts
  • src/kernels/execution/types.ts
  • src/kernels/kernelExecution.ts
  • src/kernels/kernelProvider.node.ts
  • src/messageTypes.ts
  • src/notebooks/controllers/vscodeNotebookController.ts
  • src/notebooks/deepnote/importClient.node.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthCommandHandler.node.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthCommandHandler.node.unit.test.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthCommandHandler.web.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthCommandHandler.web.unit.test.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthInvariants.unit.test.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthKernelRestartBridge.node.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthKernelRestartBridge.node.unit.test.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthOrphanedTokenCleaner.node.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthOrphanedTokenCleaner.node.unit.test.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthSqlBlockCodeGenerator.node.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthSqlBlockCodeGenerator.node.unit.test.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthTestHelpers.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthTokenStorage.node.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthTokenStorage.node.unit.test.ts
  • src/notebooks/deepnote/integrations/federatedAuth/googleOAuthProvider.node.ts
  • src/notebooks/deepnote/integrations/federatedAuth/googleOAuthProvider.node.unit.test.ts
  • src/notebooks/deepnote/integrations/federatedAuth/oauthLoopbackFlow.node.ts
  • src/notebooks/deepnote/integrations/federatedAuth/oauthLoopbackFlow.node.unit.test.ts
  • src/notebooks/deepnote/integrations/federatedAuth/vendoredBlocksHelpers.ts
  • src/notebooks/deepnote/integrations/federatedAuth/vendoredBlocksHelpers.unit.test.ts
  • src/notebooks/deepnote/integrations/integrationWebview.ts
  • src/notebooks/deepnote/integrations/integrationWebview.unit.test.ts
  • src/notebooks/deepnote/integrations/types.ts
  • src/notebooks/deepnote/openInDeepnoteHandler.node.ts
  • src/notebooks/serviceRegistry.node.ts
  • src/notebooks/serviceRegistry.web.ts
  • src/platform/common/constants.ts
  • src/platform/common/utils/localize.ts
  • src/platform/notebooks/deepnote/integrationTypes.ts
  • src/platform/notebooks/deepnote/sqlIntegrationEnvironmentVariablesProvider.ts
  • src/platform/notebooks/deepnote/sqlIntegrationEnvironmentVariablesProvider.unit.test.ts
  • src/webviews/extension-side/dataframe/dataframeController.unit.test.ts
  • src/webviews/webview-side/integrations/BigQueryForm.tsx
  • src/webviews/webview-side/integrations/IntegrationItem.tsx
  • src/webviews/webview-side/integrations/IntegrationList.tsx
  • src/webviews/webview-side/integrations/IntegrationPanel.tsx
  • src/webviews/webview-side/integrations/types.ts

Comment thread src/kernels/errors/cellExecutionOutputError.ts Outdated
Comment thread src/kernels/execution/cellExecution.federatedAuth.unit.test.ts
Comment thread src/messageTypes.ts
Comment thread src/notebooks/controllers/vscodeNotebookController.ts
Comment thread src/notebooks/deepnote/integrations/federatedAuth/federatedAuthTestHelpers.ts Outdated
Comment thread src/notebooks/deepnote/integrations/integrationWebview.ts Outdated
Comment thread src/notebooks/deepnote/integrations/integrationWebview.ts
Comment thread src/platform/notebooks/deepnote/sqlIntegrationEnvironmentVariablesProvider.ts Outdated
- Drop Microsoft header from new CellExecutionOutputError file.
- Remove unused CancellationTokenSource in federated-auth test setup.
- Extract settle-delay magic number into DEFAULT_SETTLE_DELAY_MS.
- Tie IntegrationWebviewProvider's onDidChangeTokens subscription to IDisposableRegistry so it survives provider lifetime cleanly.
- Make stale-token delete in invalidateStaleFederatedToken non-fatal so saveConfiguration still completes if deletion fails.
- Reorder @deepnote/database-integrations import in sqlIntegrationEnvironmentVariablesProvider above local imports.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tkislan tkislan marked this pull request as ready for review May 27, 2026 11:04
@tkislan tkislan requested a review from a team as a code owner May 27, 2026 11:04
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/notebooks/deepnote/integrations/integrationWebview.ts (1)

506-527: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Extract the best-effort token-delete path.

These two branches now duplicate the same delete-and-warn flow already used elsewhere in the class. A small helper will keep cleanup behavior from drifting.

As per coding guidelines: "Extract duplicate logic into helper methods to prevent drift following DRY principle".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/notebooks/deepnote/integrations/integrationWebview.ts` around lines 506 -
527, The two code paths in IntegrationWebviewProvider that call await
this.tokenStorage.delete(integrationId).catch((err) => logger.warn(...)) should
be consolidated into a small helper method (e.g., a private async
handleStaleTokenDelete(integrationId: string, reason?: string)) that performs
the delete and logs the same warn message on error and an optional info log for
the reason; replace both occurrences (the early-delete branch and the
fingerprint-change branch that compares newFingerprint !==
stored.metadataFingerprint) with calls to this helper to centralize the
delete-and-warn behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@src/notebooks/deepnote/integrations/integrationWebview.ts`:
- Around line 506-527: The two code paths in IntegrationWebviewProvider that
call await this.tokenStorage.delete(integrationId).catch((err) =>
logger.warn(...)) should be consolidated into a small helper method (e.g., a
private async handleStaleTokenDelete(integrationId: string, reason?: string))
that performs the delete and logs the same warn message on error and an optional
info log for the reason; replace both occurrences (the early-delete branch and
the fingerprint-change branch that compares newFingerprint !==
stored.metadataFingerprint) with calls to this helper to centralize the
delete-and-warn behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: bccc74c9-0c64-40db-8980-2a0fbe64f38e

📥 Commits

Reviewing files that changed from the base of the PR and between cdd519b and b46bc10.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • package.json
  • src/kernels/errors/cellExecutionOutputError.ts
  • src/kernels/execution/cellExecution.federatedAuth.unit.test.ts
  • src/notebooks/deepnote/integrations/federatedAuth/federatedAuthTestHelpers.ts
  • src/notebooks/deepnote/integrations/integrationWebview.ts
  • src/notebooks/deepnote/integrations/integrationWebview.unit.test.ts
  • src/platform/notebooks/deepnote/sqlIntegrationEnvironmentVariablesProvider.ts
💤 Files with no reviewable changes (2)
  • src/kernels/errors/cellExecutionOutputError.ts
  • src/kernels/execution/cellExecution.federatedAuth.unit.test.ts

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 27, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/notebooks/deepnote/integrations/federatedAuth/vendoredBlocksHelpers.ts (1)

31-33: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Missing \r escape remains unaddressed.

Carriage returns still aren't escaped. If value contains \r, generated Python literals may break.

Suggested fix
 export function escapePythonString(value: string): string {
-    return `'${value.replaceAll('\\', '\\\\').replaceAll("'", "\\'").replaceAll('\n', '\\n')}'`;
+    return `'${value.replaceAll('\\', '\\\\').replaceAll("'", "\\'").replaceAll('\n', '\\n').replaceAll('\r', '\\r')}'`;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/notebooks/deepnote/integrations/federatedAuth/vendoredBlocksHelpers.ts`
around lines 31 - 33, The escapePythonString function currently does not escape
carriage returns, which can break generated Python literals; update
escapePythonString to also replace '\r' with '\\r' (e.g., add a
.replaceAll('\r', '\\r') or equivalent step in the replacement chain inside the
function) while keeping the existing backslash, single-quote and newline
replacements intact so the function remains escapePythonString and preserves its
other behaviors.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@src/notebooks/deepnote/integrations/federatedAuth/vendoredBlocksHelpers.ts`:
- Around line 31-33: The escapePythonString function currently does not escape
carriage returns, which can break generated Python literals; update
escapePythonString to also replace '\r' with '\\r' (e.g., add a
.replaceAll('\r', '\\r') or equivalent step in the replacement chain inside the
function) while keeping the existing backslash, single-quote and newline
replacements intact so the function remains escapePythonString and preserves its
other behaviors.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 09f746a5-46b7-446b-8136-34aa50ac3c7f

📥 Commits

Reviewing files that changed from the base of the PR and between b46bc10 and 165b7e5.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • src/notebooks/deepnote/integrations/federatedAuth/vendoredBlocksHelpers.ts
  • src/notebooks/deepnote/integrations/integrationWebview.ts
💤 Files with no reviewable changes (1)
  • src/notebooks/deepnote/integrations/integrationWebview.ts

@tkislan tkislan merged commit dcff931 into main May 27, 2026
13 checks passed
@tkislan tkislan deleted the tk/integration-federated-auth-big-query branch May 27, 2026 12:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants