Description
Sub-issue 4a of #5145. Extract the stateful RFC 7591 DCR resolver and credential store from pkg/authserver/runner into a new shared pkg/auth/dcr package, and migrate EmbeddedAuthServer to consume it. The CLI flow at pkg/auth/discovery::PerformOAuthFlow is migrated separately in sub-issue 4b (#5219); this slice intentionally stops there to keep the diff under the project's 400-LOC / 10-file PR-size cap.
This sub-issue is the package-creation half of the parent consolidation: it stands up the destination package and wires the first consumer. AC#2 of the parent (pkg/oauthproto already holds the stateless RFC 7591 helpers) is satisfied trivially because that move predates this work. AC#1, AC#3, and AC#4 of the parent are met partially and close fully when 4b lands.
Context
The two RFC 7591 DCR client implementations identified in #5145 — pkg/authserver/runner/dcr.go (embedded authserver) and pkg/auth/discovery::PerformOAuthFlow (CLI flow) — were on a path to diverging. The embedded-authserver side had accumulated review-derived behaviours (S256 PKCE gating, RFC 7591 §3.2.1 expiry refetch, bearer-token transport with redirect refusal, panic recovery, singleflight deduplication) that the CLI side did not inherit. Lifting the shared logic into a single package is the prerequisite for the CLI to inherit those behaviours via consumer migration.
This sub-issue does the lift only, not the second migration. The package's public API still takes embedded-authserver-shaped types (*authserver.OAuth2UpstreamRunConfig, *upstream.OAuth2Config); designing a profile-neutral input type with one consumer in hand would be speculative. That refactor is the first acceptance criterion of #5219.
Dependencies: none.
Blocks: sub-issue 4b (#5219) — the CLI migration consumes the package landed here.
Acceptance Criteria
Package extraction — pkg/auth/dcr
EmbeddedAuthServer migration — pkg/authserver/runner/embeddedauthserver.go
Drift guard for duplicated resolveSecret
Defensive hardening
Cross-cutting
Out of Scope
References
Description
Sub-issue 4a of #5145. Extract the stateful RFC 7591 DCR resolver and credential store from
pkg/authserver/runnerinto a new sharedpkg/auth/dcrpackage, and migrateEmbeddedAuthServerto consume it. The CLI flow atpkg/auth/discovery::PerformOAuthFlowis migrated separately in sub-issue 4b (#5219); this slice intentionally stops there to keep the diff under the project's 400-LOC / 10-file PR-size cap.This sub-issue is the package-creation half of the parent consolidation: it stands up the destination package and wires the first consumer. AC#2 of the parent (
pkg/oauthprotoalready holds the stateless RFC 7591 helpers) is satisfied trivially because that move predates this work. AC#1, AC#3, and AC#4 of the parent are met partially and close fully when 4b lands.Context
The two RFC 7591 DCR client implementations identified in #5145 —
pkg/authserver/runner/dcr.go(embedded authserver) andpkg/auth/discovery::PerformOAuthFlow(CLI flow) — were on a path to diverging. The embedded-authserver side had accumulated review-derived behaviours (S256 PKCE gating, RFC 7591 §3.2.1 expiry refetch, bearer-token transport with redirect refusal, panic recovery, singleflight deduplication) that the CLI side did not inherit. Lifting the shared logic into a single package is the prerequisite for the CLI to inherit those behaviours via consumer migration.This sub-issue does the lift only, not the second migration. The package's public API still takes embedded-authserver-shaped types (
*authserver.OAuth2UpstreamRunConfig,*upstream.OAuth2Config); designing a profile-neutral input type with one consumer in hand would be speculative. That refactor is the first acceptance criterion of #5219.Dependencies: none.
Blocks: sub-issue 4b (#5219) — the CLI migration consumes the package landed here.
Acceptance Criteria
Package extraction —
pkg/auth/dcrpkg/auth/dcrexists.git mvso history is preserved and the diff renders as renames:pkg/authserver/runner/dcr.go→pkg/auth/dcr/resolver.gopkg/authserver/runner/dcr_store.go→pkg/auth/dcr/store.gopkg/authserver/runner/dcr_test.go→pkg/auth/dcr/resolver_test.gopkg/authserver/runner/dcr_store_test.go→pkg/auth/dcr/store_test.goDCRprefix on packagisation (e.g.DCRResolution→dcr.Resolution,DCRCredentialStore→dcr.CredentialStore,resolveDCRCredentials→dcr.ResolveCredentials,consumeResolution→dcr.ConsumeResolution,applyResolutionToOAuth2Config→dcr.ApplyResolutionToOAuth2Config,logDCRStepError→dcr.LogStepError).pkg/auth/dcr/resolver.godocuments the process-globalsingleflight.Group(it is package-level state shared by all consumers, not per-instance) and explicitly labels the profile-agnostic framing as a target state for sub-issue 4b rather than as a description of the current API shape.EmbeddedAuthServer migration —
pkg/authserver/runner/embeddedauthserver.goEmbeddedAuthServerconsumes the new package — importspkg/auth/dcrand calls the renamed exports.EmbeddedAuthServer.DCRStore()andbuildUpstreamConfigsuse the new public names (dcr.CredentialStore,dcr.ConsumeResolution,dcr.ApplyResolutionToOAuth2Config,dcr.ResolveCredentials,dcr.LogStepError) — no stale identifiers.Drift guard for duplicated
resolveSecretresolveSecretis duplicated identically inpkg/auth/dcrandpkg/authserver/runnerbecause the new package must remain reachable-from-runner but not back. Promotion to a shared helper is deferred to 4b (where a third call site emerges).TestResolveSecret/TestResolveSecretWithEnvVarsuite inpkg/auth/dcr/secret_test.gomirrors the runner-package twin's observable contract, so any future drift between the two copies fails CI on at least one side.Defensive hardening
endpointsFromMetadatanil-guards itsmetadataparameter; the function's doc comment names theoauthprotocontract it relies on.flightKeyOf(Key) stringhelper extracts the canonical key shape used by the singleflight group, so future tests and consumers can inspect it without re-deriving the format.Cross-cutting
task lint-fix— 0 issues.task license-check— clean.go test -count=1 -race ./pkg/auth/dcr/... ./pkg/authserver/...— all pass.oauthproto.RegisterClientDynamicallyintroduced. (The pre-existing CLI caller atpkg/auth/discovery/discovery.go:747is intentionally left in place for 4b to migrate; the global ban in parent AC#3 lands with 4b's CI grep guard.)Out of Scope
resolveSecretinto a shared helper — deferred to 4b once a third call site exists.oauthproto.RegisterClientDynamicallyoutsidepkg/auth/dcr— sub-issue 4b (Migrate CLI OAuth flow to pkg/auth/dcr resolver #5219).References
.claude/rules/go-style.md,.claude/rules/testing.md,.claude/rules/pr-creation.md,CLAUDE.md.