diff --git a/CHANGELOG.md b/CHANGELOG.md index dcf9654..558761c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Changed +- `cb login` now puts the activation code in the URL fragment (`#code=`) + instead of the query string. ## [3.7.1] - 2026-08-13 ### Fixed diff --git a/spec/cb/login_spec.cr b/spec/cb/login_spec.cr index fda6908..f0bc511 100644 --- a/spec/cb/login_spec.cr +++ b/spec/cb/login_spec.cr @@ -15,6 +15,7 @@ Spectator.describe CB::Login do let(process_mock) { class_mock(Process) } let(account) { Factory.account } + let(session_intent) { Factory.session_intent } before_each { ENV["CB_API_KEY"] = nil @@ -24,9 +25,11 @@ Spectator.describe CB::Login do } it "creates and stores a new session (browser)" do + expected_url = "https://#{CB::DASHBOARD_HOST}/account/verify-cli/#{session_intent.id}#code=#{session_intent.code}" + expect(lib_open_mock).to receive(:can_open_browser?).and_return(true) - expect(lib_open_mock).to receive(:run).and_return(true) - expect(client).to receive(:create_session_intent).and_return(Factory.session_intent) + expect(lib_open_mock).to receive(:run).with([expected_url]).and_return(true) + expect(client).to receive(:create_session_intent).and_return(session_intent) expect(client).to receive(:get_account).and_return(account) expect(client).to receive(:get_session_intent).and_return( Factory.session_intent(expires_at: Time.utc + 1.day, session: Factory.session) @@ -36,12 +39,15 @@ Spectator.describe CB::Login do result = action.call expect(result).to_not be_empty + expect(action.output.to_s).to contain expected_url expect(action.output.to_s.ends_with?("Logged in as #{account.email}\n")).to be_true end it "creates and stores a new session (headless)" do + expected_url = "https://#{CB::DASHBOARD_HOST}/account/verify-cli/#{session_intent.id}#code=#{session_intent.code}" + expect(lib_open_mock).to receive(:can_open_browser?).and_return(false) - expect(client).to receive(:create_session_intent).and_return(Factory.session_intent) + expect(client).to receive(:create_session_intent).and_return(session_intent) expect(client).to receive(:get_account).and_return(account) expect(client).to receive(:get_session_intent).and_return( Factory.session_intent(expires_at: Time.utc + 1.day, session: Factory.session) @@ -49,6 +55,7 @@ Spectator.describe CB::Login do result = action.call expect(result).to_not be_empty + expect(action.output.to_s).to contain expected_url expect(action.output.to_s.ends_with?("Logged in as #{account.email}\n")).to be_true end diff --git a/src/cb/login.cr b/src/cb/login.cr index d0fb156..970ad20 100644 --- a/src/cb/login.cr +++ b/src/cb/login.cr @@ -60,7 +60,8 @@ module CB # Request a session intent. si_params = Client::SessionIntentCreateParams.new(agent_name: "cb #{CB::VERSION}") session_intent = @client.create_session_intent si_params - login_url = "https://#{DASHBOARD_HOST}/account/verify-cli/#{session_intent.id}?code=#{session_intent.code}" + # Fragment (`#code=`) is not sent to the server or written to access logs. + login_url = "https://#{DASHBOARD_HOST}/account/verify-cli/#{session_intent.id}#code=#{session_intent.code}" # Start polling for completion of session authentication. poll_login_channel = Channel(LoginResult).new