Skip to content

Commit 41bda6e

Browse files
committed
delete telemetry and add portal res enum
1 parent aae5052 commit 41bda6e

File tree

9 files changed

+126
-91
lines changed

9 files changed

+126
-91
lines changed

crates/chat-cli/src/auth/builder_id.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,6 @@ impl DeviceRegistration {
210210
secret_store
211211
.set_secret(Self::SECRET_KEY, &serde_json::to_string(&self)?)
212212
.await?;
213-
info!("save builderid token");
214213
Ok(())
215214
}
216215
}

crates/chat-cli/src/auth/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ <h4>Request denied</h4>
161161
return
162162
}
163163

164-
const productName = 'KIRO CLI'
164+
const productName = 'Amazon Q for CLI'
165165
document.getElementById(
166166
'approvalMessage'
167167
).innerText = `${productName} have been given requested permissions`

crates/chat-cli/src/auth/pkce.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -319,14 +319,6 @@ impl PkceRegistration {
319319
None => Err(AuthError::OAuthMissingCode),
320320
}
321321
}
322-
323-
pub fn issuer_url(&self) -> &str {
324-
&self.issuer_url
325-
}
326-
327-
pub fn region(&self) -> &Region {
328-
&self.region
329-
}
330322
}
331323

332324
type CodeSender = std::sync::Arc<tokio::sync::mpsc::Sender<Result<(String, String), AuthError>>>;

crates/chat-cli/src/auth/portal.rs

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,21 @@ const USER_AGENT: &str = "Kiro-CLI";
4646
struct AuthPortalCallback {
4747
login_option: String,
4848
code: Option<String>,
49-
issuer_uri: Option<String>,
49+
issuer_url: Option<String>,
5050
sso_region: Option<String>,
5151
state: String,
5252
path: String,
5353
}
5454

5555
pub enum PortalResult {
56+
/// User authenticated with social provider (Google/GitHub)
5657
Social(SocialProvider),
57-
Internal { issuer_uri: String, idc_region: String },
58+
/// User selected BuilderID authentication
59+
BuilderId { issuer_url: String, idc_region: String },
60+
/// User selected AWS Identity Center authentication
61+
AwsIdc { issuer_url: String, idc_region: String },
62+
/// User selected internal authentication (Amazon-only)
63+
Internal { issuer_url: String, idc_region: String },
5864
}
5965

6066
/// Local-only: open unified portal and handle single callback
@@ -119,15 +125,41 @@ pub async fn start_unified_auth(db: &mut Database) -> Result<PortalResult, AuthE
119125
Ok(PortalResult::Social(provider))
120126
},
121127
"internal" => {
122-
let issuer_uri = callback
123-
.issuer_uri
124-
.ok_or_else(|| AuthError::OAuthCustomError("Missing issuer_uri for internal auth".into()))?;
128+
let issuer_url = callback
129+
.issuer_url
130+
.ok_or_else(|| AuthError::OAuthCustomError("Missing issuer_url for internal auth".into()))?;
125131
let sso_region = callback
126132
.sso_region
127133
.ok_or_else(|| AuthError::OAuthCustomError("Missing sso_region for internal auth".into()))?;
128-
// DO NOT register here. Let caller run start_pkce_authorization(issuer_uri, sso_region).
134+
// DO NOT register here. Let caller run start_pkce_authorization(issuer_url, sso_region).
129135
Ok(PortalResult::Internal {
130-
issuer_uri,
136+
issuer_url,
137+
idc_region: sso_region,
138+
})
139+
},
140+
"awsidc" => {
141+
let issuer_url: String = callback
142+
.issuer_url
143+
.ok_or_else(|| AuthError::OAuthCustomError("Missing issuer_url for awsIdc auth".into()))?;
144+
let sso_region = callback
145+
.sso_region
146+
.ok_or_else(|| AuthError::OAuthCustomError("Missing sso_region for awsIdc auth".into()))?;
147+
// DO NOT register here. Let caller run start_pkce_authorization(issuer_url, sso_region).
148+
Ok(PortalResult::AwsIdc {
149+
issuer_url,
150+
idc_region: sso_region,
151+
})
152+
},
153+
"builderid" => {
154+
let issuer_url = callback
155+
.issuer_url
156+
.ok_or_else(|| AuthError::OAuthCustomError("Missing issuer_url for builderId auth".into()))?;
157+
let sso_region = callback
158+
.sso_region
159+
.ok_or_else(|| AuthError::OAuthCustomError("Missing sso_region for builderId auth".into()))?;
160+
// DO NOT register here. Let caller run start_pkce_authorization(issuer_url, sso_region).
161+
Ok(PortalResult::BuilderId {
162+
issuer_url,
131163
idc_region: sso_region,
132164
})
133165
},
@@ -201,7 +233,7 @@ impl Service<Request<Incoming>> for AuthCallbackService {
201233
let callback = AuthPortalCallback {
202234
login_option: query_params.get("login_option").cloned().unwrap_or_default(),
203235
code: query_params.get("code").cloned(),
204-
issuer_uri: query_params.get("issuer_uri").cloned(),
236+
issuer_url: query_params.get("issuer_url").cloned(),
205237
sso_region: query_params.get("idc_region").cloned(),
206238
state: query_params.get("state").cloned().unwrap_or_default(),
207239
path: path.to_string(),
@@ -210,7 +242,7 @@ impl Service<Request<Incoming>> for AuthCallbackService {
210242
debug!(
211243
login_option=%callback.login_option,
212244
code_present=%callback.code.is_some(),
213-
issuer_uri=?callback.issuer_uri,
245+
issuer_url=?callback.issuer_url,
214246
state=%callback.state,
215247
"Parsed portal callback query"
216248
);

crates/chat-cli/src/cli/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,9 @@ impl Cli {
234234
},
235235
log_to_stdout: std::env::var_os("Q_LOG_STDOUT").is_some() || self.verbose > 0,
236236
log_file_path: match subcommand {
237-
RootSubcommand::Chat { .. } => Some(logs_dir().expect("home dir must be set").join("qchat.log")),
237+
RootSubcommand::Chat { .. } | RootSubcommand::Login { .. } => {
238+
Some(logs_dir().expect("home dir must be set").join("qchat.log"))
239+
},
238240
_ => None,
239241
},
240242
delete_old_log_file: false,

crates/chat-cli/src/cli/user.rs

Lines changed: 74 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -106,45 +106,27 @@ impl LoginArgs {
106106
match start_unified_auth(&mut os.database).await? {
107107
PortalResult::Social(provider) => {
108108
pre_portal_spinner.stop_with_message(format!("Logged in with {}", provider));
109-
os.telemetry.send_user_logged_in(None, None, Some(provider)).ok();
109+
os.telemetry.send_user_logged_in().ok();
110110
return Ok(ExitCode::SUCCESS);
111111
},
112-
PortalResult::Internal { issuer_uri, idc_region } => {
113-
pre_portal_spinner.stop();
114-
// EXACTLY same with original PKCE path: this registers client + completes auth.
115-
let (client, registration) =
116-
start_pkce_authorization(Some(issuer_uri.clone()), Some(idc_region.clone())).await?;
117-
118-
match crate::util::open::open_url_async(&registration.url).await {
119-
// If it succeeded, finish PKCE.
120-
Ok(()) => {
121-
let mut spinner = Spinner::new(vec![
122-
SpinnerComponent::Spinner,
123-
SpinnerComponent::Text(" Logging in...".into()),
124-
]);
125-
let issuer_url = registration.issuer_url().to_string();
126-
let region = registration.region().to_string();
127-
let ctrl_c_stream = ctrl_c();
128-
tokio::select! {
129-
res = registration.finish(&client, Some(&mut os.database)) => res?,
130-
Ok(_) = ctrl_c_stream => {
131-
#[allow(clippy::exit)]
132-
exit(1);
133-
},
134-
}
135-
os.telemetry
136-
.send_user_logged_in(Some(issuer_url), Some(region), None)
137-
.ok();
138-
spinner.stop_with_message("Logged in".into());
139-
},
140-
// If we are unable to open the link with the browser, then fallback to
141-
// the device code flow.
142-
Err(err) => {
143-
// Try device code flow.
144-
error!(%err, "Failed to open URL with browser, falling back to device code flow");
145-
try_device_authorization(os, Some(issuer_uri.clone()), Some(idc_region.clone())).await?;
146-
},
147-
}
112+
PortalResult::BuilderId { issuer_url, idc_region } => {
113+
pre_portal_spinner.stop_with_message("".into());
114+
info!("Completing BuilderID authentication");
115+
complete_sso_auth(os, issuer_url, idc_region, false).await?;
116+
},
117+
PortalResult::AwsIdc { issuer_url, idc_region } => {
118+
pre_portal_spinner.stop_with_message("".into());
119+
info!("Completing AWS Identity Center authentication");
120+
// Save IdC credentials for future use
121+
let _ = os.database.set_start_url(issuer_url.clone());
122+
let _ = os.database.set_idc_region(idc_region.clone());
123+
124+
complete_sso_auth(os, issuer_url, idc_region, true).await?;
125+
},
126+
PortalResult::Internal { issuer_url, idc_region } => {
127+
pre_portal_spinner.stop_with_message("".into());
128+
info!("Completing internal authentication");
129+
complete_sso_auth(os, issuer_url, idc_region, true).await?;
148130
},
149131
}
150132
} else {
@@ -202,6 +184,7 @@ impl LoginArgs {
202184

203185
(Some(start_url), Some(region))
204186
},
187+
AuthMethod::Social(_) => unreachable!(),
205188
};
206189

207190
// Remote machine won't be able to handle browser opening and redirects,
@@ -212,13 +195,60 @@ impl LoginArgs {
212195
select_profile_interactive(os, true).await?;
213196
}
214197
},
198+
AuthMethod::Social(_) => unreachable!(),
215199
}
216200
}
217201

218202
Ok(ExitCode::SUCCESS)
219203
}
220204
}
221205

206+
/// Complete SSO authentication (BuilderID, IdC, or Internal) after portal selection
207+
///
208+
/// # Arguments
209+
/// * `requires_profile` - Whether to prompt for profile selection after login (IdC only)
210+
async fn complete_sso_auth(os: &mut Os, issuer_url: String, idc_region: String, requires_profile: bool) -> Result<()> {
211+
let (client, registration) = start_pkce_authorization(Some(issuer_url.clone()), Some(idc_region.clone())).await?;
212+
213+
match crate::util::open::open_url_async(&registration.url).await {
214+
Ok(()) => {
215+
// Browser opened successfully, wait for PKCE flow to complete
216+
let mut spinner = Spinner::new(vec![
217+
SpinnerComponent::Spinner,
218+
SpinnerComponent::Text(" Logging in...".into()),
219+
]);
220+
221+
let ctrl_c_stream = ctrl_c();
222+
tokio::select! {
223+
res = registration.finish(&client, Some(&mut os.database)) => res?,
224+
Ok(_) = ctrl_c_stream => {
225+
#[allow(clippy::exit)]
226+
exit(1);
227+
},
228+
}
229+
230+
os.telemetry.send_user_logged_in().ok();
231+
spinner.stop_with_message("Logged in".into());
232+
233+
// Prompt for profile selection if needed (IdC only)
234+
if requires_profile {
235+
select_profile_interactive(os, true).await?;
236+
}
237+
},
238+
Err(err) => {
239+
// Failed to open browser, fallback to device code flow
240+
error!(%err, "Failed to open URL, falling back to device code flow");
241+
try_device_authorization(os, Some(issuer_url), Some(idc_region)).await?;
242+
243+
if requires_profile {
244+
select_profile_interactive(os, true).await?;
245+
}
246+
},
247+
}
248+
249+
Ok(())
250+
}
251+
222252
pub async fn logout(os: &mut Os) -> Result<ExitCode> {
223253
let _ = crate::auth::logout(&mut os.database).await;
224254
let _ = crate::auth::social::logout_social(&os.database).await;
@@ -301,7 +331,7 @@ impl WhoamiArgs {
301331

302332
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
303333
pub enum LicenseType {
304-
/// Free license (Builder ID)
334+
/// Free license with Builder ID
305335
Free,
306336
/// Pro license with Identity Center
307337
Pro,
@@ -325,13 +355,18 @@ enum AuthMethod {
325355
BuilderId,
326356
/// IdC (enterprise)
327357
IdentityCenter,
358+
/// Social login
359+
#[allow(dead_code)]
360+
Social(SocialProvider),
328361
}
329362

330363
impl Display for AuthMethod {
331364
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
332365
match self {
333366
AuthMethod::BuilderId => write!(f, "Use for Free with Builder ID"),
334367
AuthMethod::IdentityCenter => write!(f, "Use with Pro license"),
368+
AuthMethod::Social(SocialProvider::Google) => write!(f, "Use with Google"),
369+
AuthMethod::Social(SocialProvider::Github) => write!(f, "Use with GitHub"),
335370
}
336371
}
337372
}
@@ -382,7 +417,7 @@ async fn try_device_authorization(os: &mut Os, start_url: Option<String>, region
382417
{
383418
PollCreateToken::Pending => {},
384419
PollCreateToken::Complete => {
385-
os.telemetry.send_user_logged_in(start_url, region, None).ok();
420+
os.telemetry.send_user_logged_in().ok();
386421
spinner.stop_with_message("Logged in".into());
387422
break;
388423
},

crates/chat-cli/src/telemetry/core.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,12 @@ impl Event {
8282

8383
pub fn into_metric_datum(self) -> Option<MetricDatum> {
8484
match self.ty {
85-
EventType::UserLoggedIn { social_provider } => Some(
85+
EventType::UserLoggedIn {} => Some(
8686
CodewhispererterminalUserLoggedIn {
8787
create_time: self.created_time,
8888
value: None,
8989
credential_start_url: self.credential_start_url.map(Into::into),
9090
codewhispererterminal_in_cloudshell: None,
91-
social_provider: social_provider.map(Into::into),
9291
}
9392
.into_metric_datum(),
9493
),
@@ -594,9 +593,7 @@ pub struct AgentConfigInitArgs {
594593
#[serde(rename_all = "camelCase")]
595594
#[serde(tag = "type")]
596595
pub enum EventType {
597-
UserLoggedIn {
598-
social_provider: Option<String>,
599-
},
596+
UserLoggedIn {},
600597
RefreshCredentials {
601598
request_id: String,
602599
result: TelemetryResult,

crates/chat-cli/src/telemetry/mod.rs

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ use crate::api_client::{
5757
ApiClientError,
5858
};
5959
use crate::auth::builder_id::get_start_url_and_region;
60-
use crate::auth::social::SocialProvider;
6160
use crate::aws_common::app_name;
6261
use crate::cli::RootSubcommand;
6362
use crate::database::settings::Setting;
@@ -232,23 +231,8 @@ impl TelemetryThread {
232231
Ok(())
233232
}
234233

235-
pub fn send_user_logged_in(
236-
&self,
237-
start_url: Option<String>,
238-
sso_region: Option<String>,
239-
provider: Option<SocialProvider>,
240-
) -> Result<(), TelemetryError> {
241-
let mut telemetry_event = Event::new(EventType::UserLoggedIn {
242-
social_provider: provider.map(|p| p.to_string()),
243-
});
244-
if let Some(url) = start_url {
245-
telemetry_event.set_start_url(url);
246-
}
247-
if let Some(region) = sso_region {
248-
telemetry_event.set_sso_region(region);
249-
}
250-
251-
Ok(self.tx.send(telemetry_event)?)
234+
pub fn send_user_logged_in(&self) -> Result<(), TelemetryError> {
235+
Ok(self.tx.send(Event::new(EventType::UserLoggedIn {}))?)
252236
}
253237

254238
pub fn send_daily_heartbeat(&self) -> Result<(), TelemetryError> {
@@ -806,7 +790,7 @@ mod test {
806790
let thread = TelemetryThread::new(&Env::new(), &Fs::new(), &mut database)
807791
.await
808792
.unwrap();
809-
thread.send_user_logged_in(None, None, None).ok();
793+
thread.send_user_logged_in().ok();
810794
drop(thread);
811795

812796
assert!(!logs_contain("ERROR"));
@@ -825,7 +809,7 @@ mod test {
825809
.await
826810
.unwrap();
827811

828-
thread.send_user_logged_in(None, None, None).ok();
812+
thread.send_user_logged_in().ok();
829813
thread
830814
.send_cli_subcommand_executed(&database, &RootSubcommand::Version { changelog: None })
831815
.await

0 commit comments

Comments
 (0)