refactor(api): extract ResolveActiveAuth helper, eliminate auth-precedence duplication (KLA-447)#43
Merged
Merged
Conversation
…dence duplication (KLA-447) KLA-439 (jc doctor, PR #42) accumulated 13 Bugbot findings — all of one structural class. `collectAuth` in cmd/doctor.go was a hand-rolled mirror of api.NewClient()'s auth-resolution precedence, and the two implementations drifted at every edge (auth method labels, OAuth fallback to api_key, keychain-ref resolution failures, --api-key vs JC_API_KEY precedence, ...). Each fix exposed the next narrower edge because the underlying duplication remained. This change pulls auth resolution into the api package as the single source of truth: - internal/api/auth_resolve.go: new ResolveActiveAuth(Hint) returns a ResolvedAuth struct carrying Method / Source / Fingerprint / OrgID / OrgIDSource plus internal apiKey / tokenCache fields used by NewClient. The Hint struct lets cmd-package callers pass cobra pflag .Changed bits through without the api package importing pflag. - internal/api/client.go: NewClient becomes a 7-line consumer of ResolveActiveAuth — same decision tree, no duplicate precedence walking. The api package now drops its config import (resolution details are encapsulated in auth_resolve.go). - internal/cmd/doctor.go: collectAuth shrinks from 110 lines to 9. Doctor is now a pure presentation layer — Method, Source, Fingerprint, OrgID, OrgIDSource are all copied from api.ResolveActiveAuth output. Future maintainers cannot reintroduce a drift here because the precedence walking lives in one place. Behavior preservation: all 16 doctor tests pass unchanged. Plus 14 new api-package tests covering every Bugbot-found edge directly against ResolveActiveAuth, including: - TestResolveActiveAuth_KeychainRefAPIKeyDoesntCountAsFallback - TestNewClient_KeychainRefAPIKeyReturnsErrNoAPIKey That pair is the architectural-fix proof point for Bugbot finding #13 (BUGBOT_BUG_ID fef17827): the two layers now agree by construction. A service_account profile with an unresolvable keychain api_key reference produces (a) Source surfacing the keychain failure cause, (b) Method NOT claiming "api_key fallback" (because no real key resolved), and (c) NewClient returning ErrNoAPIKey. Pre-refactor, doctor said "fallback" while NewClient said "no auth." Diff: 4 files, +614 / -160. ~130 net lines removed from doctor; +267 lines of single-source-of-truth resolver + 310 lines of exhaustive tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Reviewed by Cursor Bugbot for commit 0c604ea. Configure here.
… KLA-447) Bugbot finding on PR #43 (Low). The refactored collectOrgID took a profile string argument but delegated to api.ResolveActiveAuth which uses config.ActiveProfile() internally — the argument was silently ignored. A future caller passing a non-active profile name would get wrong results. In practice the only callers were two tests in doctor_test.go, both passing the active profile (so the bug never manifested). The function was kept solely as a thin shim for those tests, with a comment promising it'd delegate consistently — which Bugbot rightly flagged as a misleading contract. Fix: delete collectOrgID and update the two tests to assert against collectAuth().OrgID / OrgIDSource — the actual production data path. Cleaner and removes the broken-contract function entirely. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jrennichjc
approved these changes
Jun 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
KLA-447 — the architectural cleanup motivated by KLA-439 / PR #42's Bugbot review trail.
KLA-439 accumulated 13 Bugbot findings, all of one structural class: `collectAuth` in `cmd/doctor.go` was a hand-rolled mirror of `api.NewClient()`'s auth-resolution precedence, and the two implementations drifted at every edge. Each fix exposed the next narrower one because the underlying duplication remained.
This change pulls auth resolution into the api package as the single source of truth. Future maintainers cannot reintroduce a drift here because the precedence walking lives in one place.
Changes
Diff: 4 files, +614 / -160. Net ~130 lines removed from doctor; gained a focused single-source-of-truth resolver with exhaustive tests.
Behavior preservation
All 16 existing doctor tests pass unchanged. That's the bar — the refactor must not regress any of the 13 Bugbot edges:
```
=== RUN TestCollectAuth_JCAPIKeyEnvWins PASS
=== RUN TestCollectAuth_FlagBeatsEnvEvenWhenEqual PASS
=== RUN TestCollectAuth_ServiceAccountReportsOAuth PASS
=== RUN TestCollectAuth_ServiceAccountWinsOverStrayEnvKey PASS
=== RUN TestCollectAuth_ServiceAccountMissingClientCreds PASS
=== RUN TestCollectAuth_ServiceAccountKeychainSecretMissing PASS
=== RUN TestCollectAuth_ServiceAccountKeychainFailureFallsBackToAPIKey PASS
=== RUN TestCollectAuth_ServiceAccountFallsBackToAPIKey PASS
=== RUN TestCollectAuth_KeychainReference PASS
=== RUN TestCollectAuth_PlaintextProfileConfig PASS
=== RUN TestCollectAuth_Unset PASS
=== RUN TestCollectProfile_* PASS (3)
=== RUN TestCollectOrgID_* PASS (2)
```
Bugbot #13 proof-point
The acceptance criteria called for a cross-package parity test. Bugbot finding #13 (`BUGBOT_BUG_ID fef17827`) was the exact case where the duplicated precedence was structurally wrong: doctor checked raw viper values, NewClient checked resolved values.
Two new paired tests in `auth_resolve_test.go`:
The two layers now agree by construction. Pre-refactor, doctor said "fallback" while NewClient said "no auth."
Test plan
🤖 Generated with Claude Code
Note
Medium Risk
Touches authentication resolution used by every API call and doctor diagnostics; behavior is intended to be preserved but edge cases (OAuth fallback, keychain refs, flag/env precedence) are security-sensitive.
Overview
Centralizes JumpCloud credential resolution in
api.ResolveActiveAuth, soNewClientandjc doctorshare one decision tree instead of duplicated precedence logic.auth_resolve.goaddsResolvedAuth(method, source, fingerprint, org ID + provenance) and optionalHint.APIKeyFlagChangedfor--api-keyvsJC_API_KEYattribution without importing cobra. The resolver covers service-account OAuth, keychain failures, silent api_key fallback labeling, and org ID precedence.NewClientnow delegates toResolveActiveAuth(~7 lines).collectAuthin doctor shrinks to mapping that result into the report struct; inlinecollectOrgIDis removed.auth_resolve_test.goadds branch coverage plus a paired test that unresolvablekeychain://api_key refs do not count as fallback andNewClientreturnsErrNoAPIKey. Doctor org-ID tests callcollectAuthinstead ofcollectOrgID.Reviewed by Cursor Bugbot for commit 1ad377c. Bugbot is set up for automated code reviews on this repo. Configure here.