@@ -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+
222252pub 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 ) ]
303333pub 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
330363impl 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 } ,
0 commit comments