login: drop ireturn nolint via concrete flow; tidy output spacing · Entire

login: drop ireturn nolint via concrete flow; tidy output spacing

ec2a59f→main· toothbrush·1mo ago·3 files·+44 added/-83 removed

Avoid the ireturn lint suppression by following "accept interfaces, return structs" (as the device-flow shim already does):

Output: one space after "Logging in to:" and a single blank line before "Waiting for sign-in..." (the leading newline doubled up with the post-Enter newline).

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

Sessions

e72efb851121View transcript

Changes

3

92 unmodified lines

// BrowserAuthFlow is one in-progress loopback authorization-code login. // It wraps an authcode.Flow so login.go can depend on a small interface // (and fake it in tests) rather than the concrete library type that owns // a live loopback listener. type BrowserAuthFlow interface { // AuthorizationURL is the URL to open in the user's browser. AuthorizationURL() string // Wait blocks until the browser is redirected to the loopback // listener, returning the authorization code. Wait(ctx context.Context) (code string, err error) // Exchange redeems code for access + refresh tokens. Exchange(ctx context.Context, code string) (accessToken, refreshToken string, err error) // Close tears down the loopback listener. Safe to call after Wait. Close() error }

// browserAuthFlow adapts *authcode.Flow to BrowserAuthFlow, flattening the // TokenSet to the (access, refresh) pair login.go persists — mirroring how // PollDeviceAuth flattens the device-flow result. type browserAuthFlow struct { // BrowserAuthFlow is one in-progress loopback authorization-code login. It // wraps an authcode.Flow, flattening the TokenSet to the (access, refresh) // pair login.go persists — mirroring how PollDeviceAuth flattens the // device-flow result. login.go depends on a small local interface that this // concrete type satisfies, so it can fake the flow in tests. type BrowserAuthFlow struct { inner *authcode.Flow }

func (f *browserAuthFlow) AuthorizationURL() string { return f.inner.AuthorizationURL }

func (f *browserAuthFlow) Wait(ctx context.Context) (string, error) { return f.inner.Wait(ctx) //nolint:wrapcheck // shim preserves the lib's wrapped errors verbatim for errors.Is }

func (f *browserAuthFlow) Exchange(ctx context.Context, code string) (string, string, error) { ts, err := f.inner.Exchange(ctx, code) if err != nil { return "", "", err //nolint:wrapcheck // shim returns authcode errors verbatim so callers can errors.Is on sentinels } return ts.AccessToken, ts.RefreshToken, nil }

func (f *browserAuthFlow) Close() error { return f.inner.Close() //nolint:wrapcheck // shutdown error is best-effort; caller logs at most }

// StartBrowserAuth begins the loopback authorization-code flow: it binds a // local listener and returns a flow carrying the browser URL to open. func (c *Client) StartBrowserAuth(ctx context.Context) (BrowserAuthFlow, error) { f, err := c.browser.Start(ctx) if err != nil { return nil, err //nolint:wrapcheck // shim returns authcode errors verbatim so callers can errors.Is on sentinels } return &browserAuthFlow{inner: f}, nil }

// BaseURL returns the issuer base URL this client talks to.

Mcmd/entire/cli/auth/client.go+17/-28

46 unmodified lines

type browserAuthClient interface { StartBrowserAuth(ctx context.Context) (auth.BrowserAuthFlow, error) BaseURL() string }

func newLoginCmd() *cobra.Command { // the same both-flows-with-fallback shape gh / gcloud / aws sso // ship. --device forces the device flow explicitly. }

// runBrowserLogin runs the loopback authorization-code flow: open the // authorization URL in the user's browser, wait for the redirect back to // the local listener, then exchange the code for tokens. func runBrowserLogin(ctx context.Context, outW, errW io.Writer, client browserAuthClient, openURL browserOpenFunc) error { flow, err := client.StartBrowserAuth(ctx) if err != nil { return fmt.Errorf("start login: %w", err) } defer func() { _ = flow.Close() }() fmt.Fprintf(outW, "Logging in to: %s\n\n", client.BaseURL()) fmt.Fprint(outW, "Press Enter to open in browser...")

code, err := flow.Wait(ctx) if err != nil { return fmt.Errorf("complete login: %w", err) } return persistLogin(outW, errW, client.BaseURL(), token, refreshToken) }

// persistLogin validates the freshly-issued access token, saves it to the

Mcmd/entire/cli/login.go+22/-18

301 unmodified lines

// fakeBrowserFlow implements auth.BrowserAuthFlow for unit tests. type fakeBrowserFlow struct { authURL string waitCode string }

// fakeBrowserClient implements browserAuthClient for unit tests. type fakeBrowserClient struct { flow *fakeBrowserFlow startErr error }

func (c *fakeBrowserClient) StartBrowserAuth(context.Context) (auth.BrowserAuthFlow, error) { if c.startErr != nil { return nil, c.startErr } return c.flow, nil }

func (c *fakeBrowserClient) BaseURL() string { return "http://test" }

func TestShouldUseBrowserLogin(t *testing.T) { t.Parallel() }