Summary
internal/auth/token_storage.go can write the auth token to the file fallback, but later reads and deletes stop checking that file as soon as the OS keyring starts returning ErrNotFound instead of a transport error.
Repro
- Run
lstk login in an environment where the system keyring backend is unavailable, so systemTokenStorage.SetAuthToken() falls back to configDir/auth-token.
- Run
lstk again later in an environment where the keyring API is reachable, but no lstk.auth-token entry exists there.
systemTokenStorage.GetAuthToken() returns ErrTokenNotFound from the keyring branch and never checks the fallback file.
lstk logout takes the same path and reports Not currently logged in, leaving the fallback token file behind.
Expected
If a token was previously stored via the file fallback, later runs should still read it and lstk logout should still remove it until that state is migrated into the keyring.
Actual
A token written by the fallback path becomes effectively orphaned once the keyring starts returning ErrNotFound:
Auth.GetToken() stops seeing it and falls through to LOCALSTACK_AUTH_TOKEN or browser login.
Auth.Logout() reports Not currently logged in and does not remove the file-backed token.
Likely Cause
internal/auth/token_storage.go, systemTokenStorage.GetAuthToken()
internal/auth/token_storage.go, systemTokenStorage.DeleteAuthToken()
internal/auth/auth.go, Auth.GetToken() / Auth.Logout()
Source / Trigger
The trigger is the storage-mode transition itself: a token is first written through the file fallback, then a later run sees a reachable-but-empty keyring.
Control Gap
SetAuthToken() already falls back to the file store on keyring errors, but GetAuthToken() treats keyring.ErrNotFound as a terminal result instead of checking whether a fallback token exists. DeleteAuthToken() has the same blind spot.
Impact
Users can get forced back through login even though a valid fallback token is still on disk, and lstk logout can leave that stale credential behind. That also means the fallback token may reappear later if the keyring becomes unavailable again.
Fix Direction
When the keyring returns ErrNotFound, also check the file fallback before returning ErrTokenNotFound, and have DeleteAuthToken() remove the fallback file in the same state. A small migration path that imports the file token into the keyring and deletes the file would also close the loop cleanly.
Summary
internal/auth/token_storage.gocan write the auth token to the file fallback, but later reads and deletes stop checking that file as soon as the OS keyring starts returningErrNotFoundinstead of a transport error.Repro
lstk loginin an environment where the system keyring backend is unavailable, sosystemTokenStorage.SetAuthToken()falls back toconfigDir/auth-token.lstkagain later in an environment where the keyring API is reachable, but nolstk.auth-tokenentry exists there.systemTokenStorage.GetAuthToken()returnsErrTokenNotFoundfrom the keyring branch and never checks the fallback file.lstk logouttakes the same path and reportsNot currently logged in, leaving the fallback token file behind.Expected
If a token was previously stored via the file fallback, later runs should still read it and
lstk logoutshould still remove it until that state is migrated into the keyring.Actual
A token written by the fallback path becomes effectively orphaned once the keyring starts returning
ErrNotFound:Auth.GetToken()stops seeing it and falls through toLOCALSTACK_AUTH_TOKENor browser login.Auth.Logout()reportsNot currently logged inand does not remove the file-backed token.Likely Cause
internal/auth/token_storage.go,systemTokenStorage.GetAuthToken()internal/auth/token_storage.go,systemTokenStorage.DeleteAuthToken()internal/auth/auth.go,Auth.GetToken()/Auth.Logout()Source / Trigger
The trigger is the storage-mode transition itself: a token is first written through the file fallback, then a later run sees a reachable-but-empty keyring.
Control Gap
SetAuthToken()already falls back to the file store on keyring errors, butGetAuthToken()treatskeyring.ErrNotFoundas a terminal result instead of checking whether a fallback token exists.DeleteAuthToken()has the same blind spot.Impact
Users can get forced back through login even though a valid fallback token is still on disk, and
lstk logoutcan leave that stale credential behind. That also means the fallback token may reappear later if the keyring becomes unavailable again.Fix Direction
When the keyring returns
ErrNotFound, also check the file fallback before returningErrTokenNotFound, and haveDeleteAuthToken()remove the fallback file in the same state. A small migration path that imports the file token into the keyring and deletes the file would also close the loop cleanly.