Skip to content

Extract DCR resolver into pkg/auth/dcr (package + EmbeddedAuthServer migration) #5220

@tgrunnagle

Description

@tgrunnagle

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 #5145pkg/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

  • New package pkg/auth/dcr exists.
  • Files moved with git mv so history is preserved and the diff renders as renames:
    • pkg/authserver/runner/dcr.gopkg/auth/dcr/resolver.go
    • pkg/authserver/runner/dcr_store.gopkg/auth/dcr/store.go
    • pkg/authserver/runner/dcr_test.gopkg/auth/dcr/resolver_test.go
    • pkg/authserver/runner/dcr_store_test.gopkg/auth/dcr/store_test.go
  • Public symbols are renamed to drop the redundant DCR prefix on packagisation (e.g. DCRResolutiondcr.Resolution, DCRCredentialStoredcr.CredentialStore, resolveDCRCredentialsdcr.ResolveCredentials, consumeResolutiondcr.ConsumeResolution, applyResolutionToOAuth2Configdcr.ApplyResolutionToOAuth2Config, logDCRStepErrordcr.LogStepError).
  • Package doc comment in pkg/auth/dcr/resolver.go documents the process-global singleflight.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.go

  • EmbeddedAuthServer consumes the new package — imports pkg/auth/dcr and calls the renamed exports.
  • Doc comments on EmbeddedAuthServer.DCRStore() and buildUpstreamConfigs use the new public names (dcr.CredentialStore, dcr.ConsumeResolution, dcr.ApplyResolutionToOAuth2Config, dcr.ResolveCredentials, dcr.LogStepError) — no stale identifiers.
  • Externally-observable DCR behaviour of the embedded authserver is unchanged: registration shape, caching, error reporting, structured logging, expiry refetch, S256 gating.

Drift guard for duplicated resolveSecret

  • resolveSecret is duplicated identically in pkg/auth/dcr and pkg/authserver/runner because 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).
  • A parallel TestResolveSecret / TestResolveSecretWithEnvVar suite in pkg/auth/dcr/secret_test.go mirrors the runner-package twin's observable contract, so any future drift between the two copies fails CI on at least one side.

Defensive hardening

  • endpointsFromMetadata nil-guards its metadata parameter; the function's doc comment names the oauthproto contract it relies on.
  • A flightKeyOf(Key) string helper 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.
  • No new direct callers of oauthproto.RegisterClientDynamically introduced. (The pre-existing CLI caller at pkg/auth/discovery/discovery.go:747 is 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

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions