Skip to content

feat: use local HTTP server for OAuth callback - AAP-70006#2695

Open
goneri wants to merge 9 commits intomainfrom
goneri/feat-use-local-HTTP-server-for-OAuth-callback_22990
Open

feat: use local HTTP server for OAuth callback - AAP-70006#2695
goneri wants to merge 9 commits intomainfrom
goneri/feat-use-local-HTTP-server-for-OAuth-callback_22990

Conversation

@goneri
Copy link
Copy Markdown
Collaborator

@goneri goneri commented Mar 27, 2026

Replace reliance on the vscode:// URI scheme handler (which is not
always functional at OS level) with a local HTTP server on a random
port to receive the OAuth redirect. This behaviour is only enabled
for the Lightspeed / SAAS endpoint.

Changes

  • Local loopback OAuth callback: start an ephemeral HTTP server on
    127.0.0.1:<random-port>/callback and use it as the redirect_uri
    when the base URI matches SAAS hosts. The vscode:// URI handler is
    used for all other endpoints.
  • Redirect URI threading: pass the actual redirectUri through the
    call chain (handleUriForCoderequestOAuthAccountFromCode)
    instead of storing it in a mutable field, avoiding cross-request
    race conditions.
  • Sign-out on config change: sign out the current OAuth session when
    the provider or API endpoint setting changes, so stale tokens are
    not reused.
  • Server listen error handling: properly reject the listen promise
    on startup failure instead of hanging indefinitely.
  • Callback page improvements: use a neutral "Authorization received"
    message (token exchange hasn't completed yet), escape HTML in error
    messages to prevent reflected markup, and return HTTP 400 on error.
  • ESLint config: allow underscore-prefixed unused vars/args
    (argsIgnorePattern: "^_", varsIgnorePattern: "^_") and remove
    now-redundant eslint-disable-next-line comments.

Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a conditional local loopback OAuth callback and propagates the effective redirect URI through the OAuth flow; introduces OAuth-session sign-out when switching LLM providers; and removes several inline ESLint-disable comments and updates tests to include the new authentication provider dependency.

Changes

Cohort / File(s) Summary
Local Loopback OAuth + redirect propagation
src/features/lightspeed/lightSpeedOAuthProvider.ts
Implements optional loopback HTTP server on 127.0.0.1:<ephemeral_port> for /callback when base_uri matches LOOPBACK_CALLBACK_HOSTS; login() races loopback code promise with VS Code URI-handler; handleUriForCode now accepts redirectUri; requestOAuthAccountFromCode posts the effective redirect_uri to /o/token/; createSession no longer forwards scopes to login.
OAuth session cleanup on provider change
src/features/lightspeed/commands/providerCommands.ts, src/features/lightspeed/vue/views/llmProviderMessageHandlers.ts, src/extension.ts
Adds signOutOAuthSessions() (private) that removes all OAuth sessions; calls inserted where LLM provider type or API endpoint changes; extension.ts passes lightSpeedAuthenticationProvider into LLM Provider panel; LlmProviderMessageHandlers receives and stores the provider dependency.
Tests updated for new dependency
test/unit/lightspeed/commands/providerCommands.test.ts, test/unit/lightspeed/views/llmProviderMessageHandlers.test.ts, test/unit/lightspeed/views/llmProviderPanel.test.ts
Mocks extended to include lightSpeedAuthenticationProvider with getSessions and removeSession to satisfy new sign-out calls in handler/commands tests.
ESLint rule and comment cleanups
eslint.config.mjs, src/features/lightspeed/ansibleContext.ts, src/features/lightspeed/explorerWebviewViewProvider.ts, src/features/lightspeed/providers/rhcustom.ts, src/features/quickLinks/utils/quickLinksViewProvider.ts, test/unit/.../*.test.ts, test/unit/lightspeed/providers/base.test.ts
Adds @typescript-eslint/no-unused-vars rule (ignores names starting with _); removes multiple inline eslint-disable comments and stray commented lines across source and tests—no behavioral changes.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Ext as Extension
  participant Auth as OAuth /o/authorize
  participant Local as Local Loopback (127.0.0.1)
  participant VSCode as VS Code URI Handler
  participant Token as OAuth /o/token

  Note over Ext,Auth: login() starts OAuth flow
  Ext->>Auth: open /o/authorize?redirect_uri=(local or external)
  User->>Auth: authenticate & authorize
  Auth-->>User: redirect to redirect_uri?code=CODE
  alt redirect to local loopback
    User->>Local: GET /callback?code=CODE
    Local->>Ext: deliver code + redirect_uri
  else redirect to VS Code handler
    User->>VSCode: vscode://.../callback?code=CODE
    VSCode->>Ext: deliver URI containing code
  end
  Ext->>Token: POST /o/token with code and effective redirect_uri
  Token-->>Ext: return tokens
  Ext->>Local: close server (if open)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A tiny server popped on the vine,
Codes zipped in, raced the VS Code line,
I caught the redirect, sent tokens through,
Closed the hole and munched a carrot too. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: implementing a local HTTP server for OAuth callbacks instead of relying solely on the vscode:// URI handler.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch goneri/feat-use-local-HTTP-server-for-OAuth-callback_22990

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ssbarnea
Copy link
Copy Markdown
Member

@goneri Not sure if this was supposed to fix the broken macos login but still does not work for me. I authorized the login and ended up on confirmation page but vscode was still waiting for confirmation and there was no way to manually finish the process.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 30, 2026

Codecov Report

❌ Patch coverage is 21.13821% with 194 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/features/lightspeed/lightSpeedOAuthProvider.ts 11.58% 145 Missing ⚠️
...lightspeed/vue/views/llmProviderMessageHandlers.ts 42.30% 28 Missing and 2 partials ⚠️
...c/features/lightspeed/commands/providerCommands.ts 42.30% 13 Missing and 2 partials ⚠️
src/extension.ts 0.00% 2 Missing ⚠️
src/features/lightspeed/ansibleContext.ts 0.00% 1 Missing ⚠️
...eatures/quickLinks/utils/quickLinksViewProvider.ts 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ssbarnea ssbarnea force-pushed the goneri/feat-use-local-HTTP-server-for-OAuth-callback_22990 branch from ffdc2ea to a9dd623 Compare March 31, 2026 10:39
Copy link
Copy Markdown

@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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/features/lightspeed/lightSpeedOAuthProvider.ts`:
- Line 95: The private field _activeRedirectUri is incorrectly shared across the
provider; remove this mutable field and keep the redirect URI scoped to a single
auth attempt by passing the redirectUri as an argument through the call
chain—update login() to obtain the redirectUri and pass it into
handleUriForCode(redirectUri) and then into
requestOAuthAccountFromCode(redirectUri) (and any intermediate helpers that
currently rely on _activeRedirectUri), and remove all reads/writes of
_activeRedirectUri so each login flow uses its own redirectUri without
cross-request mutation.
- Around line 310-320: The callback page currently says "Authentication
successful" before the token exchange and reflects raw query text into HTML;
change the user-facing success string in the res.end call to a neutral message
such as "Authorization code received; you may close this tab" (so it doesn't
claim final success before token exchange) and escape/sanitize the errorMsg
value before interpolating it into the HTML (use an HTML-escape helper or
replace &,<,>,",') to prevent reflected markup; update the res.end branch that
uses errorMsg and keep resolveCode(resolve) behavior unchanged (references:
res.writeHead, res.end, resolveCode, errorMsg).
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 37670388-a237-43ba-8134-77e23f8871f6

📥 Commits

Reviewing files that changed from the base of the PR and between afa700f and a9dd623.

📒 Files selected for processing (1)
  • src/features/lightspeed/lightSpeedOAuthProvider.ts

Comment on lines +362 to +390
const useLoopback =
LightSpeedAuthenticationProvider.LOOPBACK_CALLBACK_HOSTS.includes(
base_uri,
);

let localServer:
| Awaited<ReturnType<typeof this.startLocalCallbackServer>>
| undefined;
let redirectUri: string;

if (useLoopback) {
localServer = await this.startLocalCallbackServer();
redirectUri = localServer.redirectUri;
this._logger.debug(
`[ansible-lightspeed-oauth] Using local callback redirect URI: ${redirectUri}`,
);
} else {
redirectUri = this._externalRedirectUri;
this._logger.debug(
`[ansible-lightspeed-oauth] Using external redirect URI: ${redirectUri}`,
);
}

const searchParams = new URLSearchParams([
["response_type", "code"],
["code_challenge", getCodeChallenge()],
["code_challenge_method", "S256"],
["client_id", LIGHTSPEED_CLIENT_ID],
["redirect_uri", redirectUri],
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In OAuth 2.0 / PKCE for native apps, does a single authorization request redirect only to the one redirect_uri sent in that request, or can it effectively fall back to a second redirect URI if the first callback path fails?

💡 Result:

In OAuth 2.0 / PKCE for native apps, a single authorization request redirects only to the one redirect_uri sent in that request. The authorization server does not fallback to a second redirect URI if the first callback path fails. The server strictly validates the redirect_uri parameter against registered values for the client using exact string matching (per RFC 6749 Section 3.1.2.3 and RFC 8252 Section 8.4) and redirects the user agent exclusively to that URI if valid. If invalid or mismatched, it returns an error without redirecting. There is no provision in the protocol for automatic fallback to alternative URIs; client apps must ensure the specified redirect_uri is reachable.

Citations:


This does not provide a real vscode:// fallback.

When useLoopback is true, the authorize request at lines 385–390 specifies the loopback redirect_uri. Per OAuth 2.0 (RFC 6749 / RFC 8252), the authorization server redirects exclusively to that one registered URI—there is no automatic fallback to alternative callback handlers. If the loopback callback path fails after the browser step completes, the URI-handler branch at lines 396–400 cannot intercept it. To provide actual recovery, retry the authorization request with this._externalRedirectUri after loopback fails, or have the callback page explicitly hand off to the custom URI handler.

Also applies to: 396–400, 415–439

@goneri goneri force-pushed the goneri/feat-use-local-HTTP-server-for-OAuth-callback_22990 branch from 388995c to bc721e3 Compare March 31, 2026 13:31
@github-actions github-actions bot added feat and removed feat labels Mar 31, 2026
Copy link
Copy Markdown

@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/features/lightspeed/lightSpeedOAuthProvider.ts (1)

311-313: ⚠️ Potential issue | 🟡 Minor

Message is premature and potentially misleading.

"Authentication successful" is displayed before the token exchange (line 432) completes. If the subsequent token exchange fails, the user sees a success message in the browser but VS Code remains waiting. This may explain the issue ssbarnea reported where "the browser shows a confirmation page but VS Code remains waiting."

Consider using a neutral message that doesn't claim final success:

✏️ Suggested change
       if (code) {
         res.end(
-          "<html><body><h1>Authentication successful</h1><p>You can close this tab and return to your editor.</p></body></html>",
+          "<html><body><h1>Authorization received</h1><p>Return to your editor to finish signing in.</p></body></html>",
         );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/lightspeed/lightSpeedOAuthProvider.ts` around lines 311 - 313,
The browser message is sent via res.end(...) before the token exchange completes
(the token exchange call at line 432), which can show "Authentication
successful" even if exchange fails; change the flow so the HTTP response shows a
neutral "You may close this window" message (or wait) and only send a final
success page after the token-exchange promise resolves successfully, and send an
error/failure page if the token exchange rejects; update the code around the
res.end and the token-exchange call so the response is sent conditionally based
on the success/failure of that token exchange.
🧹 Nitpick comments (2)
src/features/lightspeed/lightSpeedOAuthProvider.ts (2)

429-435: Consider adding trace logging for OAuth flow debugging.

Given the reported issue where VS Code remains waiting after the browser shows success, adding trace logging around the token exchange could help diagnose where the flow stalls:

🔍 Suggested logging for debugging
           if (localServer) {
             candidates.push(
-              localServer.codePromise.then((code) =>
-                this.requestOAuthAccountFromCode(code, redirectUri),
-              ) as Promise<OAuthAccount>,
+              localServer.codePromise.then((code) => {
+                this._logger.debug(
+                  "[ansible-lightspeed-oauth] Authorization code received from local server, initiating token exchange...",
+                );
+                return this.requestOAuthAccountFromCode(code, redirectUri);
+              }) as Promise<OAuthAccount>,
             );
           }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/lightspeed/lightSpeedOAuthProvider.ts` around lines 429 - 435,
Add trace logging around the local OAuth token exchange to help debug where the
flow stalls: before awaiting localServer.codePromise and calling
requestOAuthAccountFromCode(…, redirectUri) log that a code was received
(include the redirectUri and a masked code), after the promise resolves log
success with the returned OAuthAccount (or its id), and on rejection log the
error stack; target the block that pushes into candidates (localServer,
codePromise, requestOAuthAccountFromCode, redirectUri) so the logs bracket the
token-exchange call and its outcome.

336-338: Consider adding error handling for server close.

server.close() can emit an error if the server is not running. For robustness, consider handling potential errors:

♻️ Optional improvement
     const close = () => {
-      server.close();
+      server.close((err) => {
+        if (err) {
+          this._logger.debug(
+            `[ansible-lightspeed-oauth] Error closing local callback server: ${err.message}`,
+          );
+        }
+      });
     };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/lightspeed/lightSpeedOAuthProvider.ts` around lines 336 - 338,
The close() helper currently calls server.close() without handling errors;
update the close function to handle potential failures from server.close by
either checking server.listening before calling or using server.close((err) => {
/* handle/log err */ }) and/or attaching a one-time 'error' handler on server to
surface errors; reference the close function and the server variable so the
change ensures errors are logged or propagated instead of being swallowed or
crashing the process.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/features/lightspeed/lightSpeedOAuthProvider.ts`:
- Around line 311-313: The browser message is sent via res.end(...) before the
token exchange completes (the token exchange call at line 432), which can show
"Authentication successful" even if exchange fails; change the flow so the HTTP
response shows a neutral "You may close this window" message (or wait) and only
send a final success page after the token-exchange promise resolves
successfully, and send an error/failure page if the token exchange rejects;
update the code around the res.end and the token-exchange call so the response
is sent conditionally based on the success/failure of that token exchange.

---

Nitpick comments:
In `@src/features/lightspeed/lightSpeedOAuthProvider.ts`:
- Around line 429-435: Add trace logging around the local OAuth token exchange
to help debug where the flow stalls: before awaiting localServer.codePromise and
calling requestOAuthAccountFromCode(…, redirectUri) log that a code was received
(include the redirectUri and a masked code), after the promise resolves log
success with the returned OAuthAccount (or its id), and on rejection log the
error stack; target the block that pushes into candidates (localServer,
codePromise, requestOAuthAccountFromCode, redirectUri) so the logs bracket the
token-exchange call and its outcome.
- Around line 336-338: The close() helper currently calls server.close() without
handling errors; update the close function to handle potential failures from
server.close by either checking server.listening before calling or using
server.close((err) => { /* handle/log err */ }) and/or attaching a one-time
'error' handler on server to surface errors; reference the close function and
the server variable so the change ensures errors are logged or propagated
instead of being swallowed or crashing the process.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5d36b7a8-deb8-44c7-9caf-73bdfd29070d

📥 Commits

Reviewing files that changed from the base of the PR and between a9dd623 and bc721e3.

📒 Files selected for processing (1)
  • src/features/lightspeed/lightSpeedOAuthProvider.ts

Replace reliance on the vscode:// URI scheme handler (which is not
always functional at OS level) with a local HTTP server on a random
port to receive the OAuth redirect. The vscode:// URI handler is
kept as a parallel fallback.

This behaviour is only enabled for the Lightspeed / SAAS endpoint.

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

@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.

Caution

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

⚠️ Outside diff range comments (1)
src/features/lightspeed/commands/providerCommands.ts (1)

117-148: ⚠️ Potential issue | 🟠 Major

Handle same-provider endpoint changes in this flow too.

Line 146 only signs out when the provider type changes. If the user re-runs this command for the same OAuth-backed provider and changes apiEndpoint in the prompts below, the old session survives even though subsequent requests target a different host. Mirror the endpoint-change check from handleSaveSettings() here, or centralize this policy so the two entry points cannot drift.

Also applies to: 153-203

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/lightspeed/commands/providerCommands.ts` around lines 117 - 148,
The flow currently only signs out OAuth sessions when the provider type changes;
update it to also detect endpoint changes like handleSaveSettings() does:
capture the previous apiEndpoint (from llmProviderSettings.getSettings() or
similar) before calling llmProviderSettings.setProvider, then after applying the
new settings/prompts compare the new apiEndpoint to the previous one and call
signOutOAuthSessions() if either the provider type changed or the apiEndpoint
changed; reference llmProviderSettings.setProvider, previousProvider,
signOutOAuthSessions, and mirror the endpoint-change logic from
handleSaveSettings() so both entry points use the same policy.
♻️ Duplicate comments (2)
src/features/lightspeed/lightSpeedOAuthProvider.ts (2)

327-329: ⚠️ Potential issue | 🟠 Major

Reject loopback bind failures explicitly.

Line 327 only waits for the successful listening path. Bind failures are emitted via 'error', so this await never rejects and the login can hang or surface as an unhandled server error instead of a user-facing failure.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/lightspeed/lightSpeedOAuthProvider.ts` around lines 327 - 329,
The current await new Promise block only resolves on server.listen's success and
ignores bind errors; update the Promise that wraps server.listen (the one using
server.listen(0, "127.0.0.1", () => resolve())) to also attach an 'error'
listener that rejects the Promise when the server emits 'error', and ensure you
remove the 'error' listener when the 'listening' callback runs to avoid leaks;
reference the same server variable and the 'listening' and 'error' events so the
login flow fails fast on bind failures instead of hanging.

373-439: ⚠️ Potential issue | 🟠 Major

This still is not a real vscode:// fallback.

Line 391 sends the authorize request with the loopback redirect_uri, so the auth server will only redirect there. The URI-handler branch at Lines 397-439 can race the same attempt, but it cannot recover a broken loopback callback path. To keep the promised fallback, retry authorization with this._externalRedirectUri after loopback failure, or have the loopback page explicitly hand off to the custom URI.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/lightspeed/lightSpeedOAuthProvider.ts` around lines 373 - 439,
The current flow starts the loopback server (startLocalCallbackServer) and opens
the authorize URI using the loopback redirect, but never retries or falls back
to the external vscode:// handler when the loopback callback fails; update the
logic around useLoopback/redirectUri, promiseFromEvent(this._uriHandler.event,
this.handleUriForCode(redirectUri)) and the Promise.race candidates so that if
the loopback path times out or rejects (including the localServer.codePromise
rejection), you programmatically retry the authorization by opening a new
authorize URI using this._externalRedirectUri and a new promiseFromEvent bound
to handleUriForCode(this._externalRedirectUri), then await that second attempt
(and ensure you cancel previous listeners via cancelWaitingForRedirectUrl and
any localServer resources) before finally rejecting after
LIGHTSPEED_SERVICE_LOGIN_TIMEOUT; also ensure requestOAuthAccountFromCode is
reused for both loopback and external CODE cases.
🧹 Nitpick comments (2)
src/features/lightspeed/vue/views/llmProviderMessageHandlers.ts (1)

9-9: Use a type-only import here.

Line 9 is only used in annotations. Keeping it as a value import adds a runtime edge into lightSpeedOAuthProvider.ts; if the build preserves value imports, that closes a new cycle back into src/extension.ts.

♻️ Proposed fix
-import { LightSpeedAuthenticationProvider } from "@src/features/lightspeed/lightSpeedOAuthProvider";
+import type { LightSpeedAuthenticationProvider } from "@src/features/lightspeed/lightSpeedOAuthProvider";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/lightspeed/vue/views/llmProviderMessageHandlers.ts` at line 9,
The import of LightSpeedAuthenticationProvider in llmProviderMessageHandlers.ts
is only used for type annotations and should be converted to a type-only import
to avoid introducing a runtime dependency and potential cycle; replace the
current value import of LightSpeedAuthenticationProvider with a type-only import
(e.g., using "import type { LightSpeedAuthenticationProvider } ...") so only the
type is emitted and no runtime edge into lightSpeedOAuthProvider.ts is created.
test/unit/lightspeed/views/llmProviderMessageHandlers.test.ts (1)

182-190: Assert the new session-cleanup path.

The mock added here is never exercised. Please add cases that verify getSessions()/removeSession() run when the provider or endpoint changes, and stay untouched when neither changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/lightspeed/views/llmProviderMessageHandlers.test.ts` around lines
182 - 190, Add unit tests in
test/unit/lightspeed/views/llmProviderMessageHandlers.test.ts that exercise the
session-cleanup path by invoking the same message/handler used for provider or
endpoint updates (e.g., call the function that handles LLM provider change
messages in this test file). Specifically, add one test that simulates changing
the provider and assert mockDeps.lightSpeedAuthenticationProvider.getSessions
and .removeSession are called, another test that simulates changing only the
endpoint and assert they are called, and a test that simulates no change and
asserts those mocks are not called; use the existing vi.fn mocks on
getSessions/removeSession, clear or reset mocks between tests (vi.clearAllMocks
or similar), and assert call counts (toHaveBeenCalled / toHaveBeenCalledTimes)
against mockDeps.lightSpeedAuthenticationProvider.getSessions and removeSession.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/features/lightspeed/commands/providerCommands.ts`:
- Around line 117-148: The flow currently only signs out OAuth sessions when the
provider type changes; update it to also detect endpoint changes like
handleSaveSettings() does: capture the previous apiEndpoint (from
llmProviderSettings.getSettings() or similar) before calling
llmProviderSettings.setProvider, then after applying the new settings/prompts
compare the new apiEndpoint to the previous one and call signOutOAuthSessions()
if either the provider type changed or the apiEndpoint changed; reference
llmProviderSettings.setProvider, previousProvider, signOutOAuthSessions, and
mirror the endpoint-change logic from handleSaveSettings() so both entry points
use the same policy.

---

Duplicate comments:
In `@src/features/lightspeed/lightSpeedOAuthProvider.ts`:
- Around line 327-329: The current await new Promise block only resolves on
server.listen's success and ignores bind errors; update the Promise that wraps
server.listen (the one using server.listen(0, "127.0.0.1", () => resolve())) to
also attach an 'error' listener that rejects the Promise when the server emits
'error', and ensure you remove the 'error' listener when the 'listening'
callback runs to avoid leaks; reference the same server variable and the
'listening' and 'error' events so the login flow fails fast on bind failures
instead of hanging.
- Around line 373-439: The current flow starts the loopback server
(startLocalCallbackServer) and opens the authorize URI using the loopback
redirect, but never retries or falls back to the external vscode:// handler when
the loopback callback fails; update the logic around useLoopback/redirectUri,
promiseFromEvent(this._uriHandler.event, this.handleUriForCode(redirectUri)) and
the Promise.race candidates so that if the loopback path times out or rejects
(including the localServer.codePromise rejection), you programmatically retry
the authorization by opening a new authorize URI using this._externalRedirectUri
and a new promiseFromEvent bound to handleUriForCode(this._externalRedirectUri),
then await that second attempt (and ensure you cancel previous listeners via
cancelWaitingForRedirectUrl and any localServer resources) before finally
rejecting after LIGHTSPEED_SERVICE_LOGIN_TIMEOUT; also ensure
requestOAuthAccountFromCode is reused for both loopback and external CODE cases.

---

Nitpick comments:
In `@src/features/lightspeed/vue/views/llmProviderMessageHandlers.ts`:
- Line 9: The import of LightSpeedAuthenticationProvider in
llmProviderMessageHandlers.ts is only used for type annotations and should be
converted to a type-only import to avoid introducing a runtime dependency and
potential cycle; replace the current value import of
LightSpeedAuthenticationProvider with a type-only import (e.g., using "import
type { LightSpeedAuthenticationProvider } ...") so only the type is emitted and
no runtime edge into lightSpeedOAuthProvider.ts is created.

In `@test/unit/lightspeed/views/llmProviderMessageHandlers.test.ts`:
- Around line 182-190: Add unit tests in
test/unit/lightspeed/views/llmProviderMessageHandlers.test.ts that exercise the
session-cleanup path by invoking the same message/handler used for provider or
endpoint updates (e.g., call the function that handles LLM provider change
messages in this test file). Specifically, add one test that simulates changing
the provider and assert mockDeps.lightSpeedAuthenticationProvider.getSessions
and .removeSession are called, another test that simulates changing only the
endpoint and assert they are called, and a test that simulates no change and
asserts those mocks are not called; use the existing vi.fn mocks on
getSessions/removeSession, clear or reset mocks between tests (vi.clearAllMocks
or similar), and assert call counts (toHaveBeenCalled / toHaveBeenCalledTimes)
against mockDeps.lightSpeedAuthenticationProvider.getSessions and removeSession.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8e793d82-4ede-41df-9e05-505af21a3e8d

📥 Commits

Reviewing files that changed from the base of the PR and between 0430c60 and 29ca18d.

📒 Files selected for processing (14)
  • eslint.config.mjs
  • src/extension.ts
  • src/features/lightspeed/ansibleContext.ts
  • src/features/lightspeed/commands/providerCommands.ts
  • src/features/lightspeed/explorerWebviewViewProvider.ts
  • src/features/lightspeed/lightSpeedOAuthProvider.ts
  • src/features/lightspeed/providers/rhcustom.ts
  • src/features/lightspeed/vue/views/llmProviderMessageHandlers.ts
  • src/features/quickLinks/utils/quickLinksViewProvider.ts
  • test/unit/lightspeed/commands/providerCommands.test.ts
  • test/unit/lightspeed/providers/base.test.ts
  • test/unit/lightspeed/providers/rhcustom.test.ts
  • test/unit/lightspeed/views/llmProviderMessageHandlers.test.ts
  • test/unit/lightspeed/views/llmProviderPanel.test.ts
💤 Files with no reviewable changes (1)
  • src/features/lightspeed/providers/rhcustom.ts
✅ Files skipped from review due to trivial changes (6)
  • src/features/quickLinks/utils/quickLinksViewProvider.ts
  • src/features/lightspeed/explorerWebviewViewProvider.ts
  • src/features/lightspeed/ansibleContext.ts
  • test/unit/lightspeed/providers/base.test.ts
  • test/unit/lightspeed/providers/rhcustom.test.ts
  • test/unit/lightspeed/commands/providerCommands.test.ts

- Use neutral "Authorization received" message on callback page since
  token exchange hasn't completed yet
- Escape HTML in error messages to prevent reflected markup
- Handle server.listen() failures by attaching an error listener so the
  promise rejects instead of hanging indefinitely
- Only set up the vscode:// URI handler when not using loopback, since
  OAuth redirects exclusively to the one redirect_uri in the request

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

@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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/features/lightspeed/lightSpeedOAuthProvider.ts`:
- Around line 311-322: The non-null assertion in the escapeHtml function should
be removed; update the replacement callback in escapeHtml (the function defined
as const escapeHtml = (value: string) => ...) to return a safe fallback instead
of using "![char]" — e.g., look up the character in the mapping and if undefined
return the original char (or a default escape) so the linter rule is satisfied
while behavior is unchanged. Ensure the mapping object remains the same and only
the indexing/return expression is changed to use a safe fallback.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8d16ae26-572c-4412-9d2b-47607e73a94f

📥 Commits

Reviewing files that changed from the base of the PR and between 29ca18d and 1eb28ad.

📒 Files selected for processing (1)
  • src/features/lightspeed/lightSpeedOAuthProvider.ts

Move writeHead into each branch so the error path returns 400 instead
of 200, as suggested in PR review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

3 participants