# auth: burn all PAT machinery; sessions/logout target entire-core only

`f8390f9`·

toothbrush·1mo ago·8 files·+316 added/-340 removed

entire.io's ent_personal-access-token surface (/api/v1/auth/tokens) is being sunset, and the CLI never used it for auth — it authenticates with the core JWT. Remove every trace from this repo:

- Drop the dead Provider.AuthTokensPath field (and its /api/v1/auth/tokens values + tests); nothing references the entire.io PAT path anymore.
- Rename the api.Client session plumbing off PAT-era naming: api/auth_tokens.go -> api/sessions.go, WithAuthTokensPath -> WithSessionsPath, authTokensPath -> sessionsPath, errAuthTokensPathUnset -> errSessionsPathUnset.
- Scrub PAT / ent_ / personal-access-token mentions from comments.

Session management (auth status liveness via /me, logout revocation) targets entire-core's /api/auth/tokens on the auth host (api.AuthBaseURL()) with the session-scoped core JWT — never entire.io's PAT endpoint, so the 400 from that endpoint cannot recur.

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

## Sessions

5969a20f41e3View transcript

## Changes

8

- cmd/entire/cli

- api

- Dauth_tokens.go-103

- Dauth_tokens_test.go-200

- Mclient.go+11/-15

- Asessions.go+100

- Asessions_test.go+200

- Mauth.go+5/-6

- auth

- Mprovider.go-12

- Mprovider_test.go-4

```go
package api

import (
	"context"
	"errors"
	"fmt"
	"net/url"
)

// Session is a single active login session — an OAuth refresh-token family —
// returned by the auth-tokens endpoint. One is created per `entire login`,
// across all of a user's devices. Plaintext token values are never returned by
// the server, only metadata. (The wire endpoint is historically named
// "tokens"; these rows are sessions, not personal access tokens.)
type Session struct {
	ID         string  `json:"id"`
	UserID     string  `json:"user_id"`
	Name       string  `json:"name"`
	Scope      string  `json:"scope"`
	ExpiresAt  string  `json:"expires_at"`
	LastUsedAt *string `json:"last_used_at"`
	CreatedAt  string  `json:"created_at"`
}

// SessionsResponse is the envelope returned by the list endpoint. The wire key
// stays "tokens" — that is the server's contract.
type SessionsResponse struct {
	Sessions []Session `json:"tokens"`
}

// errAuthTokensPathUnset surfaces when a session method is called on a
// Client that wasn't given a base path. Construct via
// NewClientWithBaseURL(...).WithAuthTokensPath(...) — the
// active path lives in cmd/entire/cli/auth.CurrentProvider().AuthTokensPath,
// the single source of truth for provider-version routing.
var errAuthTokensPathUnset = errors.New("api: auth-tokens path is unset (call (*Client).WithAuthTokensPath before list/revoke)")

func (c *Client) authTokensBasePath() (string, error) {
	if c.authTokensPath == "" {
		return "", errAuthTokensPathUnset
	}
	return c.authTokensPath, nil
}

// ListSessions returns the authenticated user's active login sessions.
func (c *Client) ListSessions(ctx context.Context) ([]Session, error) {
	base, err := c.authTokensBasePath()
	if err != nil {
		return nil, fmt.Errorf("list sessions: %w", err)
	}
	resp, err := c.Get(ctx, base)
	if err != nil {
		return nil, fmt.Errorf("list sessions: %w", err)
	}
	defer resp.Body.Close()

if err := CheckResponse(resp); err != nil {
		return nil, fmt.Errorf("list sessions: %w", err)
	}

var out SessionsResponse
	if err := DecodeJSON(resp, &out); err != nil {
		return nil, fmt.Errorf("list sessions: %w", err)
	}
	return out.Sessions, nil
}

// RevokeCurrentSession revokes the login session this client is authenticating
// with (the family the current bearer belongs to).
func (c *Client) RevokeCurrentSession(ctx context.Context) error {
	base, err := c.authTokensBasePath()
	if err != nil {
		return fmt.Errorf("revoke current session: %w", err)
	}
	resp, err := c.Delete(ctx, base+"/current")
	if err != nil {
		return fmt.Errorf("revoke current session: %w", err)
	}
	defer resp.Body.Close()

if err := CheckResponse(resp); err != nil {
		return fmt.Errorf("revoke current session: %w", err)
	}
	return nil
}

// RevokeSession revokes the login session with the given id.
func (c *Client) RevokeSession(ctx context.Context, id string) error {
	base, err := c.authTokensBasePath()
	if err != nil {
		return fmt.Errorf("revoke session %s: %w", id, err)
	}
	resp, err := c.Delete(ctx, base+"/"+url.PathEscape(id))
	if err != nil {
		return fmt.Errorf("revoke session %s: %w", id, err)
	}
	defer resp.Body.Close()

if err := CheckResponse(resp); err != nil {
		return fmt.Errorf("revoke session %s: %w", id, err)
	}
	return nil
}
```

// TestClient_RevokeCurrentSession_SendsDeleteWithBearer tests the deletion of the current session.
func TestClient_RevokeCurrentSession_SendsDeleteWithBearer(t *testing.T) {
	// test code here
}

// TestClient_RevokeSession_ReturnsHTTPErrorOn401 tests the HTTP 401 response on revoke.
func TestClient_RevokeSession_ReturnsHTTPErrorOn401(t *testing.T) {
	// test code here
}

// TestClient_ListSessions_ReturnsHTTPErrorOn401 tests the HTTP 401 response on list sessions.
func TestClient_ListSessions_ReturnsHTTPErrorOn401(t *testing.T) {
	// test code here
}

// Constants and functions for testing and API.
const coreSessionsPath = "/api/auth/tokens"

func newSessionsClient(token string) *api.Client {
	return api.NewClientWithBaseURL(token, api.AuthBaseURL())
		.WithAuthTokensPath(coreSessionsPath)
		.WithSessionsPath(coreSessionsPath)
}
