Skip to content

Remove browser flow as auth method#36

Merged
carole-lavillonniere merged 2 commits intomainfrom
carole/drg-530
Feb 19, 2026
Merged

Remove browser flow as auth method#36
carole-lavillonniere merged 2 commits intomainfrom
carole/drg-530

Conversation

@carole-lavillonniere
Copy link
Collaborator

@carole-lavillonniere carole-lavillonniere commented Feb 16, 2026

  • Remove browser flow auth method, keep device flow only
  • Improve the UI for authentication

Reasons for removing browser flow:

  • It feels less modern and secure to users
  • It is less secure
  • It's hard to know when to fallback to device flow
  • Handling 2 login methods makes the code more complicated

Output:
image

@carole-lavillonniere
Copy link
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 16, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@carole-lavillonniere carole-lavillonniere marked this pull request as ready for review February 16, 2026 15:28
@coderabbitai
Copy link

coderabbitai bot commented Feb 16, 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

Replaces the callback-server device flow with an interactive login provider: renames browserLogin→login, introduces newLoginProvider/loginProvider that emits a verification URL and code, optionally opens the browser, waits for user confirmation, exchanges tokens, updates tests, and deletes the old browser-callback integration test.

Changes

Cohort / File(s) Summary
Auth core
internal/auth/auth.go, internal/auth/auth_test.go
Renamed struct field browserLoginlogin, switched initialization to newLoginProvider(...), and updated tests to wire login.
Login flow implementation
internal/auth/login.go
Replaced callback-server device flow with loginProvider flow: create auth request, print URL/code, prompt to open browser, wait for confirmation, call completeAuth to exchange token; renamed types/constructors/methods and removed callback-server code.
Integration tests
test/integration/login_test.go, test/integration/login_browser_flow_test.go (deleted)
Updated integration test interactions and output expectations to match the new prompt-driven browser flow; deleted the old browser-callback integration test.
UI input handling
internal/ui/app.go
Changed pending-input behavior: Enter selects default option; single-character input can match and select corresponding option; pending state cleared and prompt hidden after selection.

Sequence Diagram(s)

sequenceDiagram
    participant CLI
    participant LoginProvider
    participant PlatformAPI
    participant Browser
    participant Keyring

    CLI->>LoginProvider: Login(ctx)
    LoginProvider->>PlatformAPI: CreateAuthRequest()
    PlatformAPI-->>LoginProvider: auth request (verification_url, user_code, request_id)
    LoginProvider-->>CLI: print URL and code, prompt to open browser
    CLI->>Browser: open verification_url (optional)
    Browser->>PlatformAPI: user authenticates & confirms request
    PlatformAPI-->>LoginProvider: request confirmed
    LoginProvider->>PlatformAPI: ExchangeAuthRequest(request_id)
    PlatformAPI-->>LoginProvider: access token / license token
    LoginProvider->>Keyring: store token
    LoginProvider-->>CLI: "Login successful"
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • silv-io
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: removing the browser flow authentication method in favor of device flow.
Description check ✅ Passed The PR description clearly explains the changes: removing browser flow auth, keeping device flow only, and improving UI, with specific reasons for the removal.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch carole/drg-530

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

Copy link

@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

🤖 Fix all issues with AI agents
In `@internal/auth/login.go`:
- Around line 49-61: The code ignores errors from reader.ReadString and
browser.OpenURL; update the interactive flow in the function that constructs
reader and uses authURL (the reader variable, browser.OpenURL call, and
subsequent reader.ReadString before output.EmitLog) to check and handle errors:
capture and handle the first ReadString error (treat io.EOF as empty response
and return or log a user-facing message for non-interactive shells), check the
error returned by browser.OpenURL and emit a warning via output.EmitLog (using
l.sink) if it fails, and also handle the second ReadString error before polling
for auth status so the function fails fast with a clear log rather than silently
continuing.
🧹 Nitpick comments (1)
test/integration/login_test.go (1)

99-108: Consider removing fixed sleeps; stdin buffering already handles ordering.

Fixed sleeps add flakiness and latency. Writing to stdin immediately should still work and speeds the test.

♻️ Suggested cleanup
-	// Wait for instructions
-	time.Sleep(100 * time.Millisecond)
@@
-	// Wait a bit for browser to open
-	time.Sleep(100 * time.Millisecond)
@@
-	// Wait for instructions to be printed
-	time.Sleep(100 * time.Millisecond)
@@
-	// Wait a bit for browser to open
-	time.Sleep(100 * time.Millisecond)

Also applies to: 167-176

Copy link

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

🧹 Nitpick comments (1)
test/integration/login_test.go (1)

116-121: Remove duplicate assertions.

Lines 119-120 are redundant:

  • "Auth request confirmed" is a substring of "Auth request confirmed, exchanging for token" (already asserted on line 117)
  • "Fetching license token" is asserted twice (lines 118 and 120)
♻️ Proposed fix to remove duplicates
 		// Should complete authentication successfully
 		assert.Contains(t, output, "Checking if auth request is confirmed")
 		assert.Contains(t, output, "Auth request confirmed, exchanging for token")
 		assert.Contains(t, output, "Fetching license token")
-		assert.Contains(t, output, "Auth request confirmed")
-		assert.Contains(t, output, "Fetching license token")
 		assert.Contains(t, output, "Login successful")

Copy link

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

🧹 Nitpick comments (1)
test/integration/login_test.go (1)

67-130: Trim self-explanatory comments in the new interaction steps.

The new inline comments mostly restate the code; consider removing them or replacing with rationale that isn’t obvious from the code itself.

🧹 Suggested cleanup
-	// Simulate answering "Y" to open browser
 	_, err = stdinPipe.Write([]byte("Y\n"))
 	require.NoError(t, err)

-	// Simulate pressing ENTER after completing auth in browser
 	_, err = stdinPipe.Write([]byte("\n"))
 	require.NoError(t, err)

 	select {
 	case out := <-outputCh:
 		output := string(out)
-		// Should show instructions
 		assert.Contains(t, output, "Verification code:")
 		assert.Contains(t, output, "TEST123")
 		assert.Contains(t, output, "Open browser now? [Y/n]")
 		assert.Contains(t, output, "Waiting for authentication (Press [ENTER] when complete)")
-		// Should complete authentication successfully
 		assert.Contains(t, output, "Checking if auth request is confirmed")
 		assert.Contains(t, output, "Auth request confirmed, exchanging for token")
 		assert.Contains(t, output, "Fetching license token")
 		assert.Contains(t, output, "Login successful")
-	// Simulate answering "Y" to open browser
 	_, err = stdinPipe.Write([]byte("Y\n"))
 	require.NoError(t, err)

-	// Simulate pressing ENTER after "completing" auth in browser
 	_, err = stdinPipe.Write([]byte("\n"))
 	require.NoError(t, err)

 	select {
 	case out := <-outputCh:
 		output := string(out)
 		assert.Contains(t, output, "Verification code:")
 		assert.Contains(t, output, "Open browser now? [Y/n]")
 		assert.Contains(t, output, "Waiting for authentication (Press [ENTER] when complete)")
-		// Should attempt authentication but fail because request not confirmed
 		assert.Contains(t, output, "Checking if auth request is confirmed")
 		assert.Contains(t, output, "auth request not confirmed")

As per coding guidelines, "Avoid adding comments for self-explanatory code; only comment when the 'why' isn't obvious from the code itself".

Also applies to: 132-185

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

In `@test/integration/login_test.go` around lines 67 - 130, In TestLoginSuccess,
remove or shorten the self-explanatory inline comments that merely restate the
code (e.g., comments around licenseToken/env check, mockServer creation, stdin
handling, output assertions) and replace any that remain with brief rationale
explaining non-obvious intent; focus on keeping clarifying comments for why we
manipulate stdinPipe, the purpose of
envWithout("LOCALSTACK_AUTH_TOKEN")/createMockAPIServer, or timing choices for
context.WithTimeout, but delete comments that repeat what functions like
cleanup(), createMockAPIServer, exec.CommandContext, stdinPipe.Write, and the
assert.Contains checks already express.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@test/integration/login_test.go`:
- Around line 67-130: In TestLoginSuccess, remove or shorten the
self-explanatory inline comments that merely restate the code (e.g., comments
around licenseToken/env check, mockServer creation, stdin handling, output
assertions) and replace any that remain with brief rationale explaining
non-obvious intent; focus on keeping clarifying comments for why we manipulate
stdinPipe, the purpose of
envWithout("LOCALSTACK_AUTH_TOKEN")/createMockAPIServer, or timing choices for
context.WithTimeout, but delete comments that repeat what functions like
cleanup(), createMockAPIServer, exec.CommandContext, stdinPipe.Write, and the
assert.Contains checks already express.

Copy link

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

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

Inline comments:
In `@internal/auth/auth.go`:
- Around line 44-45: Change the misleading log string passed to output.EmitLog
so it doesn't say "Opening browser..."; update the message in the call where
output.EmitLog(a.sink, "...") is used (near the authentication sequence invoking
a.login.Login(ctx)) to a neutral instruction such as "Authentication required.
Please complete authentication using the provided device flow instructions" or
similar wording that does not imply an automatic browser open.

In `@internal/auth/login.go`:
- Around line 29-41: loginProvider.Login currently only logs authReq.Code so
users lack the actionable verification URL; update the AuthRequest struct to
include a VerificationURL (or VerificationURI) field and change
platformClient.CreateAuthRequest to populate it, then modify loginProvider.Login
to emit a clear message via output.EmitLog and include the full URL and short
instructions (e.g., "Visit {authReq.VerificationURL} and enter code
{authReq.Code}") and update the prompt passed to output.EmitUserInputRequest
accordingly so callers and users see both the URL and code; ensure all other
uses of AuthRequest and CreateAuthRequest are updated to handle the new field.

@carole-lavillonniere carole-lavillonniere force-pushed the carole/drg-530 branch 2 times, most recently from d2865e6 to 3177af7 Compare February 18, 2026 16:08
Copy link
Member

@silv-io silv-io left a comment

Choose a reason for hiding this comment

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

Love a good cleanup!

Just a thought on expanding the userinputevent functionality. The output nit we can handle later. We'll freshen it up either way now that we default to the TUI.

Also, for posterity, this is how it looks with TUI now:


    ▟████▖    LocalStack (lstk)
   ▟██▙█▙█▟   dev
     ▀▛▀▛▀

  Authentication required. Opening browser...
  Visit: https://app.localstack.cloud/auth/request/...
  Verification code: 3FE3
  Checking if auth request is confirmed...
  Auth request confirmed, exchanging for token...
  Fetching license token...
  Login successful.
  Pulling localstack/localstack-pro:latest...
    latest: Pulling from localstack/localstack-pro
    : Digest: sha256:dab91774aa81fc565ff8019962749810d2184f28f4c7acbea514c9754235de9f
    : Status: Image is up to date for localstack/localstack-pro:latest
  localstack-aws: validating license (4.13.2.dev137)
  Starting localstack-aws...
  Waiting for localstack-aws to be ready...
  localstack-aws ready (containerId: c5326e33aa84)

@@ -42,7 +42,7 @@ func (a *Auth) GetToken(ctx context.Context) (string, error) {
}

output.EmitLog(a.sink, "Authentication required. Opening browser...")
Copy link
Member

Choose a reason for hiding this comment

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

nit: Maybe we can rename this log now, because we're asking later if we want to open it

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch, I lost this when resolving conflicts

}
w.WriteHeader(http.StatusOK)
tokenCh <- token
// Ask whether to open the browser; ENTER or Y accepts (default yes), N skips
Copy link
Member

Choose a reason for hiding this comment

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

thought: we might want to change the type a bit so that we can codify the default option more clearly. But for now this is fine

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Agreed.

a.inputPrompt = a.inputPrompt.Hide()
return a, responseCmd
}
}
Copy link
Member

Choose a reason for hiding this comment

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

nice that this works so well :)

@carole-lavillonniere carole-lavillonniere merged commit 6230f5b into main Feb 19, 2026
7 checks passed
@carole-lavillonniere carole-lavillonniere deleted the carole/drg-530 branch February 19, 2026 08:47
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

Comments