auth: drop `auth list`, show active sessions in `auth status` · Entire
auth: drop auth list, show active sessions in auth status
0f84383·
toothbrush·1mo ago·2 files·+74 added/-142 removed
Remove the entire auth list command. The rows it listed are server-side login sessions (OAuth refresh-token families), not personal access tokens — nothing functional depends on listing them (see COR-389 notes). Fold that view into entire auth status as a clearly-labelled "Active sessions" table, reusing the table renderer.
auth revoke <id> still works; the session IDs now come from auth status.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Sessions
1774a9e75c37View transcript
Changes
2
cmd/entire/cli
Mauth.go+48/-84
Mauth_test.go+26/-58
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"github.com/spf13/cobra"
)
// authTokenLister lists API tokens for the authenticated user. The
// implementation resolves its own data-API bearer via
// auth.TokenForResource (RFC 8693 exchange in split-host setups, same-
// host shortcut otherwise); callers don't pass a bearer through, which
// removes the temptation to forward the wrong-audience keyring token.
// authTokenLister lists the authenticated user's active login sessions —
// the server-side refresh-token families (one per `entire login`, across
// all devices), surfaced by `entire auth status` and used by revoke as a
// liveness probe. Despite the api.Token name, these are sessions, not
// personal access tokens; the CLI never mints them. The implementation
// resolves its own data-API bearer via auth.TokenForResource (RFC 8693
// exchange in split-host setups, same-host shortcut otherwise); callers
// don't pass a bearer through, which removes the temptation to forward the
// wrong-audience keyring token.
type authTokenLister func(ctx context.Context) ([]api.Token, error)
// authTokenRevoker revokes a single API token by id. Same bearer-
// (same implementation as above)
// defaultListTokens fetches the authenticated user's active login sessions
// from the server. See authTokenLister for what these rows actually are.
func defaultListTokens(ctx context.Context) ([]api.Token, error) {
token, err := resolveDataAPIToken(ctx)
if err != nil {
return nil, err
}
tokens, err := list(ctx)
sessions, err := list(ctx)
if err != nil {
if isKeychainTokenRejected(err) {
fmt.Fprintf(w, "Token in keychain for %s is no longer valid.\n", baseURL)
}
}
return tokens, nil
}
// --- list -------------------------------------------------------------------
func newAuthListCmd() *cobra.Command {
var jsonOut bool
var insecureHTTPAuth bool
cmd := &cobra.Command{
Use: "list",
Short: "List active API tokens for the authenticated user",
RunE: func(cmd *cobra.Command, _ []string) error {
if err := requireSecureBaseURL(insecureHTTPAuth); err != nil {
return err
}
return runAuthList(cmd.Context(), cmd.OutOrStdout(),
auth.NewContextStore(), defaultListTokens, api.AuthBaseURL(), jsonOut)
},
}
cmd.Flags().BoolVar(&jsonOut, "json", false, "Print tokens as JSON")
addInsecureHTTPAuthFlag(cmd, &insecureHTTPAuth)
return cmd
}
func runAuthList(ctx context.Context, w io.Writer, store tokenStore, list authTokenLister, baseURL string, jsonOut bool) error {
token, err := store.GetToken(baseURL)
if err != nil {
return fmt.Errorf("read keychain: %w", err)
}
if token == "" {
return fmt.Errorf("not logged in to %s; run 'entire login' first", baseURL)
}
tokens, err := list(ctx)
if err != nil {
return err
}
if jsonOut {
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
if err := enc.Encode(tokens); err != nil {
return fmt.Errorf("encode JSON: %w", err)
}
}
if len(tokens) == 0 {
fmt.Fprintln(w, "No active tokens.")
return nil
}
fmt.Fprintln(w, "Active sessions:")
sortSessionsByRecency(tokens)
renderSessionsTable(w, newSessionsTableStyles(w), tokens, time.Now())
return nil
}
// sortSessionsByRecency orders sessions most-recently-used first
// ....
// renderSessionsTable prints a styled, column-aligned table of login sessions.
// ....