auth: remove unused RemoveAllContexts · Entire
auth: remove unused RemoveAllContexts
74c24b2·
toothbrush·1mo ago·2 files·+0 added/-80 removed
Dead since logout --all was redefined to revoke server-side sessions rather than nuke all local contexts. Nothing else references it.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Sessions
7dc263eaaac8View transcript
Changes
2
cmd/entire/cli/auth
Mcontext_store.go-29
- Mcontexts_test.go-51
61 unmodified lines
return nil
}
// RemoveAllContexts deletes every stored context and its keyring token — a
// full local logout. Returns the number of contexts removed. Best-effort on
the keyring deletes; the contexts.json clear is what makes the CLI fully
logged out.
func RemoveAllContexts() (int, error) {
var removed int
if err := contexts.Modify(contexts.DefaultConfigDir(), func(f *contexts.File) (bool, error) {
if len(f.Contexts) == 0 && f.CurrentContext == "" {
return false, nil
}
for _, c := range f.Contexts {
if c.KeychainService != "" && c.Handle != "" {
// Delete both slots: the access token and its paired refresh
// token. Dropping the refresh slot is what actually scrubs the
// machine — otherwise a leftover refresh token outlives logout.
_ = tokenstore.Delete(c.KeychainService, c.Handle) //nolint:errcheck // best-effort; the contexts.json clear below is authoritative
_ = tokenstore.Delete(tokenstore.RefreshService(c.KeychainService), c.Handle) //nolint:errcheck // best-effort; absent refresh slot is fine
}
removed++
}
f.Contexts = nil
f.CurrentContext = ""
return true, nil
}); err != nil {
return 0, fmt.Errorf("remove all contexts: %w", err)
}
return removed, nil
}
// SetCurrentContext makes name the active context. Returns an error when
// no context with that name exists (a stale current pointer is a foot-gun).
func SetCurrentContext(name string) error {
Mcmd/entire/cli/auth/context_store.go-29
268 unmodified lines
func TestRemoveAllContexts(t *testing.T) {
cfgDir := t.TempDir()
t.Setenv("ENTIRE_CONFIG_DIR", cfgDir)
restore := tokenstore.UseFileBackendForTesting(filepath.Join(t.TempDir(), "tokens.json"))
t.Cleanup(restore)
exp := time.Now().Add(time.Hour).Unix()
if _, err := RecordLoginContext(makeJWT(t, fmt.Sprintf(`{"iss":"https://a.example.com","handle":"alice","exp":%d}`, exp)), "entr_a", true); err != nil {
t.Fatalf("record a: %v", err)
}
if _, err := RecordLoginContext(makeJWT(t, fmt.Sprintf(`{"iss":"https://b.example.com","handle":"bob","exp":%d}`, exp)), "entr_b", true); err != nil {
t.Fatalf("record b: %v", err)
}
n, err := RemoveAllContexts()
if err != nil {
t.Fatalf("RemoveAllContexts: %v", err)
}
if n != 2 {
t.Fatalf("removed %d, want 2", n)
}
f, err := contexts.Load(cfgDir)
if err != nil {
t.Fatalf("load: %v", err)
}
if len(f.Contexts) != 0 || f.CurrentContext != "" {
t.Fatalf("expected fully cleared, got contexts=%d current=%q", len(f.Contexts), f.CurrentContext)
}
// Every refresh slot must be gone too, for both removed contexts.
for _, tc := range []struct{ iss, handle string }{
{"https://a.example.com", "alice"},
{"https://b.example.com", "bob"},
} {
svc := tokenstore.CoreKeyringService(tc.iss)
if v, err := tokenstore.Get(svc, tc.handle); !errors.Is(err, tokenstore.ErrNotFound) {
t.Fatalf("access slot for %s survived: value=%q err=%v", tc.handle, v, err)
}
if v, err := tokenstore.Get(tokenstore.RefreshService(svc), tc.handle); !errors.Is(err, tokenstore.ErrNotFound) {
t.Fatalf("refresh slot for %s survived: value=%q err=%v", tc.handle, v, err)
}
}
// Idempotent.
n2, err := RemoveAllContexts()
if err != nil {
t.Fatalf("second RemoveAllContexts: %v", err)
}
if n2 != 0 {
t.Fatalf("second call removed %d, want 0", n2)
}
}
func TestRemoveCurrentContext_DoesNotSwitchToAnother(t *testing.T) {
cfgDir := t.TempDir()
t.Setenv("ENTIRE_CONFIG_DIR", cfgDir)