@@ -874,6 +874,17 @@ pub struct UnauthorizedRecovery {
874874 mode : UnauthorizedRecoveryMode ,
875875}
876876
877+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
878+ pub struct UnauthorizedRecoveryStepResult {
879+ auth_state_changed : Option < bool > ,
880+ }
881+
882+ impl UnauthorizedRecoveryStepResult {
883+ pub fn auth_state_changed ( & self ) -> Option < bool > {
884+ self . auth_state_changed
885+ }
886+ }
887+
877888impl UnauthorizedRecovery {
878889 fn new ( manager : Arc < AuthManager > ) -> Self {
879890 let cached_auth = manager. auth_cached ( ) ;
@@ -917,7 +928,46 @@ impl UnauthorizedRecovery {
917928 !matches ! ( self . step, UnauthorizedRecoveryStep :: Done )
918929 }
919930
920- pub async fn next ( & mut self ) -> Result < ( ) , RefreshTokenError > {
931+ pub fn unavailable_reason ( & self ) -> & ' static str {
932+ if !self
933+ . manager
934+ . auth_cached ( )
935+ . as_ref ( )
936+ . is_some_and ( CodexAuth :: is_chatgpt_auth)
937+ {
938+ return "not_chatgpt_auth" ;
939+ }
940+
941+ if self . mode == UnauthorizedRecoveryMode :: External
942+ && !self . manager . has_external_auth_refresher ( )
943+ {
944+ return "no_external_refresher" ;
945+ }
946+
947+ if matches ! ( self . step, UnauthorizedRecoveryStep :: Done ) {
948+ return "recovery_exhausted" ;
949+ }
950+
951+ "ready"
952+ }
953+
954+ pub fn mode_name ( & self ) -> & ' static str {
955+ match self . mode {
956+ UnauthorizedRecoveryMode :: Managed => "managed" ,
957+ UnauthorizedRecoveryMode :: External => "external" ,
958+ }
959+ }
960+
961+ pub fn step_name ( & self ) -> & ' static str {
962+ match self . step {
963+ UnauthorizedRecoveryStep :: Reload => "reload" ,
964+ UnauthorizedRecoveryStep :: RefreshToken => "refresh_token" ,
965+ UnauthorizedRecoveryStep :: ExternalRefresh => "external_refresh" ,
966+ UnauthorizedRecoveryStep :: Done => "done" ,
967+ }
968+ }
969+
970+ pub async fn next ( & mut self ) -> Result < UnauthorizedRecoveryStepResult , RefreshTokenError > {
921971 if !self . has_next ( ) {
922972 return Err ( RefreshTokenError :: Permanent ( RefreshTokenFailedError :: new (
923973 RefreshTokenFailedReason :: Other ,
@@ -931,8 +981,17 @@ impl UnauthorizedRecovery {
931981 . manager
932982 . reload_if_account_id_matches ( self . expected_account_id . as_deref ( ) )
933983 {
934- ReloadOutcome :: ReloadedChanged | ReloadOutcome :: ReloadedNoChange => {
984+ ReloadOutcome :: ReloadedChanged => {
935985 self . step = UnauthorizedRecoveryStep :: RefreshToken ;
986+ return Ok ( UnauthorizedRecoveryStepResult {
987+ auth_state_changed : Some ( true ) ,
988+ } ) ;
989+ }
990+ ReloadOutcome :: ReloadedNoChange => {
991+ self . step = UnauthorizedRecoveryStep :: RefreshToken ;
992+ return Ok ( UnauthorizedRecoveryStepResult {
993+ auth_state_changed : Some ( false ) ,
994+ } ) ;
936995 }
937996 ReloadOutcome :: Skipped => {
938997 self . step = UnauthorizedRecoveryStep :: Done ;
@@ -946,16 +1005,24 @@ impl UnauthorizedRecovery {
9461005 UnauthorizedRecoveryStep :: RefreshToken => {
9471006 self . manager . refresh_token_from_authority ( ) . await ?;
9481007 self . step = UnauthorizedRecoveryStep :: Done ;
1008+ return Ok ( UnauthorizedRecoveryStepResult {
1009+ auth_state_changed : Some ( true ) ,
1010+ } ) ;
9491011 }
9501012 UnauthorizedRecoveryStep :: ExternalRefresh => {
9511013 self . manager
9521014 . refresh_external_auth ( ExternalAuthRefreshReason :: Unauthorized )
9531015 . await ?;
9541016 self . step = UnauthorizedRecoveryStep :: Done ;
1017+ return Ok ( UnauthorizedRecoveryStepResult {
1018+ auth_state_changed : Some ( true ) ,
1019+ } ) ;
9551020 }
9561021 UnauthorizedRecoveryStep :: Done => { }
9571022 }
958- Ok ( ( ) )
1023+ Ok ( UnauthorizedRecoveryStepResult {
1024+ auth_state_changed : None ,
1025+ } )
9591026 }
9601027}
9611028
0 commit comments