auth: review follow-ups — docs, single revoke func, comment renames · Entire
auth: review follow-ups — docs, single revoke func, comment renames
af7e7cd·
Soph·1mo ago·4 files·+23 added/-58 removed
- CLAUDE.md: correct the
authcommand surface (login/logout/status/ contexts/use; drop the removed list/revoke) and document logout's --everywhere / --all-contexts flags. - logout: have runLogout take a single caller-selected revoke func instead of both revokeCurrent+revokeAll plus an
allbool; the command already knows --everywhere. Drop the now-obsolete TestRunLogout_AllRevokesAllSessions (selection is covered end-to-end by TestLogoutCommand_FlagMatrix) and update the simple callers. - Fix stale method names in //nolint:wrapcheck comments left over from the Session -> AuthSession rename.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Sessions
7bbdd7fd89c1View transcript
Changes
4
MCLAUDE.md+3/-1
cmd/entire/cli
Mauth.go+1/-1
Mlogout.go+13/-19
Mlogout_test.go+6/-37
32 unmodified lines
33
34
35
36
36
37
38
39
40
41
32 unmodified lines
- `configure`: bare prints help and a hint pointing at `entire agent`; flags manage non-agent settings (telemetry, git-hook installation mode, strategy options, summary provider). Agent CRUD lives under `entire agent`.
- `auth`: `login`, `logout`, `status`, `list`, `revoke`
- `auth`: `login`, `logout`, `status`, `contexts`, `use`. `logout` takes `--everywhere` (revoke every session on the active core, not just the current one) and `--all-contexts` (log out of every saved login)
- `doctor`: bare runs the scan-and-fix flow, plus `trace`, `logs`, `bundle`
Top-level lifecycle and standalone commands: `enable`, `disable`, `status`,
MCLAUDE.md+3/-1
267 unmodified lines
268
269
270
271
271
272
273
274
267 unmodified lines
// defaultListAuthSessions lists the user's active login sessions on coreURL.
func defaultListAuthSessions(ctx context.Context, coreURL, token string) ([]api.AuthSession, error) {
return newAuthSessionsClient(coreURL, token).ListAuthSessions(ctx) //nolint:wrapcheck // ListSessions already wraps with action context
return newAuthSessionsClient(coreURL, token).ListAuthSessions(ctx) //nolint:wrapcheck // ListAuthSessions already wraps with action context
}
// runAuthStatus reports auth state against the target core: GET /me validates
Mcmd/entire/cli/auth.go+1/-1
78 unmodified lines
79
80
81
82
83
84
85
86
82
83
84
85
89
90
86
87
88
89
90
27 unmodified lines
118
119
120
124
121
122
123
124
2 unmodified lines
127
128
129
133
130
131
132
133
137
134
135
136
137
4 unmodified lines
142
143
144
148
149
150
151
152
145
146
147
148
149
150
151
152
153
1 unmodified line
155
156
157
160
161
162
163
158
159
160
78 unmodified lines
return fmt.Errorf("context core URL check: %w", err)
}
}
revokeCurrent := func(ctx context.Context) error {
return revokeCurrentAuthSession(ctx, target.coreURL, target.token)
}
revokeAll := func(ctx context.Context) error {
return revokeAllAuthSessions(ctx, target.coreURL, target.token)
}
revoke := func(ctx context.Context) error {
return revokeForTarget(ctx, target.coreURL, target.token)
}
if err := runLogout(cmd.Context(), outW, errW,
auth.NewContextStore(), revokeCurrent, revokeAll,
auth.RemoveCurrentContext, api.AuthBaseURL(), everywhere); err != nil {
auth.NewContextStore(), revoke,
auth.RemoveCurrentContext, api.AuthBaseURL()); err != nil {
return err
}
promoteNextLogin(outW, errW)
27 unmodified lines
// revokeCurrentAuthSession revokes the active session on coreURL (the family the bearer belongs to) — the default `entire logout`.
func revokeCurrentAuthSession(ctx context.Context, coreURL, token string) error {
return newAuthSessionsClient(coreURL, token).RevokeCurrentAuthSession(ctx) //nolint:wrapcheck // RevokeCurrentSession already wraps with action context
return newAuthSessionsClient(coreURL, token).RevokeCurrentAuthSession(ctx) //nolint:wrapcheck // RevokeCurrentAuthSession already wraps with action context
}
// revokeAllAuthSessions revokes every active login session on coreURL (the
2 unmodified lines
// failure, so one stuck session doesn't strand the rest.
func revokeAllAuthSessions(ctx context.Context, coreURL, token string) error {
client := newAuthSessionsClient(coreURL, token)
// ListSessions and RevokeSession already wrap with their own action
// ListAuthSessions and RevokeAuthSession already wrap with their own action
// context (incl. the session id), so return their errors verbatim.
sessions, err := client.ListAuthSessions(ctx)
if err != nil {
return err //nolint:wrapcheck // ListSessions already wraps with "list sessions"
return err //nolint:wrapcheck // ListAuthSessions already wraps with "list sessions"
}
var firstErr error
for _, s := range sessions {
4 unmodified lines
return firstErr
}
// runLogout ends the user's login. revokeCurrent revokes just the active
// session; revokeAll (used when all is set) revokes every session on the
// active core. Either way the local keyring entry and active context are
// removed, so the CLI reports logged-out even if the server call fails.
func runLogout(ctx context.Context, outW, errW io.Writer, store tokenStore, revokeCurrent, revokeAll revokeCurrentFunc, clearContext clearContextFunc, baseURL string, all bool) error {
// runLogout ends the user's login. revoke is the caller-selected server-side
// revocation — just the active session, or every session on the active core
// when --everywhere is set. Either way the local keyring entry and active
// context are removed, so the CLI reports logged-out even if the server call fails.
func runLogout(ctx context.Context, outW, errW io.Writer, store tokenStore, revoke revokeCurrentFunc, clearContext clearContextFunc, baseURL string) error {
token, err := store.GetToken(baseURL)
if err != nil {
// Fall through to the local delete: we still want the keyring entry
1 unmodified line
fmt.Fprintf(errW, "Warning: failed to read token before revocation: %v\n", err)
}
if token != "" {
revoke := revokeCurrent
if all {
revoke = revokeAll
}
if err := revoke(ctx); err != nil && !api.IsHTTPErrorStatus(err, http.StatusUnauthorized) {
// Best-effort: a transient network error shouldn't block local
// logout. A 401 means the token is already invalid server-side,
Mcmd/entire/cli/logout.go+13/-19
66 unmodified lines
67
68
69
70
70
71
72
73
24 unmodified lines
98
99
100
101
101
102
103
104
20 unmodified lines
125
126
127
128
128
129
130
131
23 unmodified lines
155
156
157
158
158
159
160
161
22 unmodified lines
184
185
186
187
187
188
189
190
19 unmodified lines
210
211
212
213
213
214
215
216
5 unmodified lines
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
225
226
227
66 unmodified lines
}
var out, errOut bytes.Buffer
err := runLogout(context.Background(), &out, &errOut, store, revoke, func(context.Context) error { return nil }, func() error { return nil }, "https://entire.io", false)
err := runLogout(context.Background(), &out, &errOut, store, revoke, func() error { return nil }, "https://entire.io")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
24 unmodified lines
}
var out, errOut bytes.Buffer
err := runLogout(context.Background(), &out, &errOut, store, revoke, func(context.Context) error { return nil }, func() error { return nil }, "https://entire.io", false)
err := runLogout(context.Background(), &out, &errOut, store, revoke, func() error { return nil }, "https://entire.io")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
20 unmodified lines
}
var out, errOut bytes.Buffer
err := runLogout(context.Background(), &out, &errOut, store, revoke, func(context.Context) error { return nil }, func() error { return nil }, "https://entire.io", false)
err := runLogout(context.Background(), &out, &errOut, store, revoke, func() error { return nil }, "https://entire.io")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
23 unmodified lines
}
var out, errOut bytes.Buffer
err := runLogout(context.Background(), &out, &errOut, store, revoke, func(context.Context) error { return nil }, func() error { return nil }, "https://entire.io", false)
err := runLogout(context.Background(), &out, &errOut, store, revoke, func() error { return nil }, "https://entire.io")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
22 unmodified lines
}
var out, errOut bytes.Buffer
err := runLogout(context.Background(), &out, &errOut, store, revoke, func(context.Context) error { return nil }, func() error { return nil }, "https://entire.io", false)
err := runLogout(context.Background(), &out, &errOut, store, revoke, func() error { return nil }, "https://entire.io")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
19 unmodified lines
revoke := func(context.Context) error { return nil }
var out, errOut bytes.Buffer
err := runLogout(context.Background(), &out, &errOut, store, revoke, func(context.Context) error { return nil }, func() error { return nil }, "https://entire.io", false)
err := runLogout(context.Background(), &out, &errOut, store, revoke, func(context.Context) error { return nil }, "https://entire.io")
if err == nil {
t.Fatal("expected error, got nil")
}
5 unmodified lines
}
}
func TestRunLogout_AllRevokesAllSessions(t *testing.T) {
t.Parallel()
store := newMockTokenStore()
store.tokens["https://entire.io"] = testLogoutToken
currentCalled, allCalled := false, false
revokeCurrent := func(context.Context) error { currentCalled = true; return nil }
revokeAll := func(context.Context) error { allCalled = true; return nil }
var out, errOut bytes.Buffer
err := runLogout(context.Background(), &out, &errOut, store,
revokeCurrent, revokeAll, func() error { return nil }, "https://entire.io", true)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if currentCalled {
t.Error("--everywhere should not call the current-session revoke")
}
if !allCalled {
t.Error("--everywhere should call the revoke-all path")
}
if !store.deleted["https://entire.io"] {
t.Fatal("local token should still be deleted under --everywhere")
}
if !strings.Contains(out.String(), "Logged out.") {
t.Fatalf("stdout = %q, want to contain %q", out.String(), "Logged out.")
}
}
func TestLogoutCmd_IsRegistered(t *testing.T) {
t.Parallel()