# auth: show user profile in `auth status` via core GET /me

`3b28626`→[main](/content/gh/entireio/cli/commits/main/index.html)·

toothbrush·1mo ago·2 files·+195 added/-49 removed

`entire auth status` now calls the core API's GET /me, which both validates
the stored token (liveness) and supplies a profile header:

Logged in to https://us.auth.entire.io
User: Alice Smith (@alice) <alice@example.com>
Identity: github/alice
Token: stored in OS keychain

Active sessions:
...

/me is the primary liveness gate (a 401 surfaces as
*coreapi.ErrorModelStatusCode, now recognised by isKeychainTokenRejected).
The active-sessions list runs after, on the data API; since the token is
already known good, a list failure degrades to a stderr warning instead of
failing the command. Empty profile fields are omitted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

## Sessions

5ca10643dc53View transcript

Refactor Auth Commands for Session Management

## Changes

2

- cmd/entire/cli

- Mauth.go+96/-14

- Mauth\_test.go+99/-35

```
12 unmodified lines

```

// authProfile is the subset of the core API's GET /me that `entire auth
// status` renders.
type authProfile struct {
	Handle         string
	DisplayName    string
	Email          string
	Provider       string
	ProviderUserID string
}

// profileFetcher fetches the logged-in user's profile via GET /me on the core
// API. Injected so status stays unit-testable without a live core.
type profileFetcher func(ctx context.Context) (*authProfile, error)

// defaultFetchProfile fetches the current user's profile from the core API's
// GET /me. It doubles as the liveness check for `entire auth status`: a 401
// (or an expired login that can't be exchanged) means the stored token is no
// longer usable, which isKeychainTokenRejected maps to a re-login hint.
func defaultFetchProfile(ctx context.Context) (*authProfile, error) {
	client, err := coreapi.New()
	if err != nil {
		return nil, fmt.Errorf("connect to Entire control plane: %w", err)
	}
	me, err := client.GetMe(ctx)
	if err != nil {
		return nil, fmt.Errorf("fetch profile: %w", err)
	}
	p := &authProfile{
		Provider:       me.Auth.Provider,
		ProviderUserID: me.Auth.ProviderUserId,
	}
	p.Handle, _ = me.Global.Handle.Get()
	if reg, ok := me.Regional.Get(); ok {
		p.DisplayName, _ = reg.DisplayName.Get()
		p.Email, _ = reg.Email.Get()
	}
	return p, nil
}

// writeProfileLines renders the user identity from GET /me as aligned
// label/value lines, omitting any field the server didn't populate.
func writeProfileLines(w io.Writer, p *authProfile) {
	var parts []string
	if p.DisplayName != "" {
		parts = append(parts, p.DisplayName)
	}
	if p.Handle != "" {
		parts = append(parts, "@"+p.Handle)
	}
	if p.Email != "" {
		parts = append(parts, "<"+p.Email+">")
	}
	if len(parts) > 0 {
		fmt.Fprintf(w, "  %-9s %s\n", "User:", strings.Join(parts, " "))
	}
	if p.Provider != "" {
		identity := p.Provider
		if p.ProviderUserID != "" {
			identity += "/" + p.ProviderUserID
		}
		fmt.Fprintf(w, "  %-9s %s\n", "Identity:", identity)
	}
}

// sortSessionsByRecency orders sessions most-recently-used first, then most
// recently created, then by id — a fully specified order independent of the
// server's response ordering.
