@@ -302,7 +302,10 @@ impl BuilderIdToken {
302302 }
303303
304304 /// Load the token from the keychain, refresh the token if it is expired and return it
305- pub async fn load ( database : & Database ) -> Result < Option < Self > , AuthError > {
305+ pub async fn load (
306+ database : & Database ,
307+ telemetry : Option < & crate :: telemetry:: TelemetryThread > ,
308+ ) -> Result < Option < Self > , AuthError > {
306309 // Can't use #[cfg(test)] without breaking lints, and we don't want to require
307310 // authentication in order to run ChatSession tests. Hence, adding this here with cfg!(test)
308311 if cfg ! ( test) {
@@ -328,7 +331,7 @@ impl BuilderIdToken {
328331
329332 if token. is_expired ( ) {
330333 trace ! ( "token is expired, refreshing" ) ;
331- token. refresh_token ( & client, database, & region) . await
334+ token. refresh_token ( & client, database, & region, telemetry ) . await
332335 } else {
333336 trace ! ( ?token, "found a valid token" ) ;
334337 Ok ( Some ( token) )
@@ -357,6 +360,7 @@ impl BuilderIdToken {
357360 client : & Client ,
358361 database : & Database ,
359362 region : & Region ,
363+ telemetry : Option < & crate :: telemetry:: TelemetryThread > ,
360364 ) -> Result < Option < Self > , AuthError > {
361365 let Some ( refresh_token) = & self . refresh_token else {
362366 warn ! ( "no refresh token was found" ) ;
@@ -416,6 +420,25 @@ impl BuilderIdToken {
416420 let display_err = DisplayErrorContext ( & err) ;
417421 error ! ( "Failed to refresh builder id access token: {}" , display_err) ;
418422
423+ // Send telemetry for refresh failure
424+ if let Some ( telemetry) = telemetry {
425+ let auth_method = match self . token_type ( ) {
426+ TokenType :: BuilderId => "BuilderId" ,
427+ TokenType :: IamIdentityCenter => "IdentityCenter" ,
428+ } ;
429+ let oauth_flow = match self . oauth_flow {
430+ OAuthFlow :: DeviceCode => "DeviceCode" ,
431+ OAuthFlow :: Pkce => "PKCE" ,
432+ } ;
433+ let error_code = match & err {
434+ SdkError :: ServiceError ( service_err) => service_err. err ( ) . meta ( ) . code ( ) . map ( |s| s. to_string ( ) ) ,
435+ _ => None ,
436+ } ;
437+ telemetry
438+ . send_auth_failed ( auth_method, oauth_flow, "TokenRefresh" , error_code)
439+ . ok ( ) ;
440+ }
441+
419442 // if the error is the client's fault, clear the token
420443 if let SdkError :: ServiceError ( service_err) = & err {
421444 if !service_err. err ( ) . is_slow_down_exception ( ) {
@@ -471,11 +494,7 @@ impl BuilderIdToken {
471494 }
472495
473496 pub fn token_type ( & self ) -> TokenType {
474- match & self . start_url {
475- Some ( url) if url == START_URL => TokenType :: BuilderId ,
476- None => TokenType :: BuilderId ,
477- Some ( _) => TokenType :: IamIdentityCenter ,
478- }
497+ token_type_from_start_url ( self . start_url . as_deref ( ) )
479498 }
480499
481500 /// Check if the token is for the internal amzn start URL (`https://amzn.awsapps.com/start`),
@@ -486,6 +505,14 @@ impl BuilderIdToken {
486505 }
487506}
488507
508+ pub fn token_type_from_start_url ( start_url : Option < & str > ) -> TokenType {
509+ match start_url {
510+ Some ( url) if url == START_URL => TokenType :: BuilderId ,
511+ None => TokenType :: BuilderId ,
512+ Some ( _) => TokenType :: IamIdentityCenter ,
513+ }
514+ }
515+
489516pub enum PollCreateToken {
490517 Pending ,
491518 Complete ,
@@ -498,6 +525,7 @@ pub async fn poll_create_token(
498525 device_code : String ,
499526 start_url : Option < String > ,
500527 region : Option < String > ,
528+ telemetry : & crate :: telemetry:: TelemetryThread ,
501529) -> PollCreateToken {
502530 let region = region. clone ( ) . map_or ( OIDC_BUILDER_ID_REGION , Region :: new) ;
503531 let client = client ( region. clone ( ) ) ;
@@ -538,6 +566,20 @@ pub async fn poll_create_token(
538566 } ,
539567 Err ( err) => {
540568 error ! ( ?err, "Failed to poll for builder id token" ) ;
569+
570+ // Send telemetry for device code failure
571+ let auth_method = match token_type_from_start_url ( start_url. as_deref ( ) ) {
572+ TokenType :: BuilderId => "BuilderId" ,
573+ TokenType :: IamIdentityCenter => "IdentityCenter" ,
574+ } ;
575+ let error_code = match & err {
576+ SdkError :: ServiceError ( service_err) => service_err. err ( ) . meta ( ) . code ( ) . map ( |s| s. to_string ( ) ) ,
577+ _ => None ,
578+ } ;
579+ telemetry
580+ . send_auth_failed ( auth_method, "DeviceCode" , "NewLogin" , error_code)
581+ . ok ( ) ;
582+
541583 PollCreateToken :: Error ( err. into ( ) )
542584 } ,
543585 }
@@ -550,7 +592,7 @@ pub async fn is_logged_in(database: &mut Database) -> bool {
550592 return true ;
551593 }
552594
553- match BuilderIdToken :: load ( database) . await {
595+ match BuilderIdToken :: load ( database, None ) . await {
554596 Ok ( Some ( _) ) => true ,
555597 Ok ( None ) => {
556598 info ! ( "not logged in - no valid token found" ) ;
@@ -585,7 +627,7 @@ pub async fn logout(database: &mut Database) -> Result<(), AuthError> {
585627pub async fn get_start_url_and_region ( database : & Database ) -> ( Option < String > , Option < String > ) {
586628 // NOTE: Database provides direct methods to access the start_url and region, but they are not
587629 // guaranteed to be up to date in the chat session. Example: login is changed mid-chat session.
588- let token = BuilderIdToken :: load ( database) . await ;
630+ let token = BuilderIdToken :: load ( database, None ) . await ;
589631 match token {
590632 Ok ( Some ( t) ) => ( t. start_url , t. region ) ,
591633 _ => ( None , None ) ,
@@ -603,7 +645,7 @@ impl ResolveIdentity for BearerResolver {
603645 ) -> IdentityFuture < ' a > {
604646 IdentityFuture :: new_boxed ( Box :: pin ( async {
605647 let database = Database :: new ( ) . await ?;
606- match BuilderIdToken :: load ( & database) . await ? {
648+ match BuilderIdToken :: load ( & database, None ) . await ? {
607649 Some ( token) => Ok ( Identity :: new (
608650 Token :: new ( token. access_token . 0 . clone ( ) , Some ( token. expires_at . into ( ) ) ) ,
609651 Some ( token. expires_at . into ( ) ) ,
@@ -618,7 +660,7 @@ pub async fn is_idc_user(database: &Database) -> Result<bool> {
618660 if cfg ! ( test) {
619661 return Ok ( false ) ;
620662 }
621- if let Ok ( Some ( token) ) = BuilderIdToken :: load ( database) . await {
663+ if let Ok ( Some ( token) ) = BuilderIdToken :: load ( database, None ) . await {
622664 Ok ( token. token_type ( ) == TokenType :: IamIdentityCenter )
623665 } else {
624666 Err ( eyre ! ( "No auth token found - is the user signed in?" ) )
0 commit comments