Skip to content

feat(auth): add TokenFetcher interface and token abstraction - #1141

Closed
TerryHowe wants to merge 2 commits into
oras-project:mainfrom
TerryHowe:feat/auth-token-abstraction
Closed

feat(auth): add TokenFetcher interface and token abstraction#1141
TerryHowe wants to merge 2 commits into
oras-project:mainfrom
TerryHowe:feat/auth-token-abstraction

Conversation

@TerryHowe

Copy link
Copy Markdown
Member

Summary

  • Adds TokenFetcher interface with Distribution, OAuth2, and Composite implementations extracted from the auth client monolith
  • Replaces exported ForceAttemptOAuth2 field with unexported legacyMode + public SetLegacyMode() setter for cleaner API
  • Adds ForceBasicAuth field to force HTTP Basic auth regardless of registry-advertised scheme
  • Allows callers to inject a custom TokenFetcher for advanced bearer token scenarios

Details

The auth client previously had all token-fetching logic inlined. This PR extracts it into a TokenFetcher interface, making it composable and testable independently:

  • TokenFetcher — interface with FetchToken(ctx, TokenParams, Credential) (string, error)
  • DistributionTokenFetcher — fetches tokens via the distribution spec (basic auth to token endpoint)
  • OAuth2TokenFetcher — fetches tokens via OAuth2 password/refresh grant
  • CompositeTokenFetcher — selects between the above based on credential type

The Client struct gains a TokenFetcher field; if set, it is used instead of the built-in logic. The legacyMode field (replacing ForceAttemptOAuth2) preserves backward-compatible behavior for registries that don't support OAuth2.

Depends on

Test plan

  • token.go fully covered by token_test.go
  • client_test.go updated for SetLegacyMode() / ForceBasicAuth API
  • Run go test ./registry/remote/auth/...

🤖 Generated with Claude Code

Introduce TokenFetcher interface with Distribution, OAuth2, and Composite
implementations extracted from the auth client monolith. Adds:
- token.go: TokenFetcher interface, TokenParams, and concrete implementations
- token_test.go: full coverage for all TokenFetcher implementations
- client.go: TokenFetcher field, ForceBasicAuth field, SetLegacyMode(),
  replace exported ForceAttemptOAuth2 with unexported legacyMode
- client_test.go, scope_test.go, example_test.go: update for API changes

Depends on: internal/authtype (already merged)

Signed-off-by: Terry Howe <terrylhowe@gmail.com>
@codecov

codecov Bot commented Apr 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.09091% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.20%. Comparing base (d04d858) to head (5ec2f0a).

Files with missing lines Patch % Lines
registry/remote/auth/token.go 87.75% 6 Missing and 6 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1141      +/-   ##
==========================================
+ Coverage   83.05%   83.20%   +0.14%     
==========================================
  Files          81       82       +1     
  Lines        5684     5793     +109     
==========================================
+ Hits         4721     4820      +99     
- Misses        595      600       +5     
- Partials      368      373       +5     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@TerryHowe TerryHowe added the v3 Things belongs to version 3.x label Apr 7, 2026
@TerryHowe

Copy link
Copy Markdown
Member Author

Splitting this PR into three smaller, individually reviewable PRs as discussed:

Closing this PR in favor of the split. Please review the new PRs in that order — #1180 first since #1181 depends on it.

@TerryHowe TerryHowe closed this May 23, 2026
TerryHowe added a commit to TerryHowe/oras-go that referenced this pull request Jun 22, 2026
Add a ForceBasicAuth bool field to auth.Client. When true, a Bearer
challenge from the registry is rewritten to a Basic challenge before
credential handling. This is useful for registries that advertise Bearer
auth but also accept HTTP Basic for the same credential pair.

Implementation is a three-line scheme rewrite in Client.Do(), applied
right after parseChallenge() and before the scheme switch, so the rest
of the basic-auth path runs unchanged. The override bypasses bearer
flow entirely, so it does not interact with the token-fetching logic.

Refs oras-project#1141 (split: PR C of A/B/C). Independent of PRs A and B.

Signed-off-by: Terry Howe <terrylhowe@gmail.com>
TerryHowe added a commit to TerryHowe/oras-go that referenced this pull request Jun 22, 2026
…acyMode

Replace the exported ForceAttemptOAuth2 bool field on auth.Client with an
unexported legacyMode field plus a public SetLegacyMode(bool) setter,
and flip the default behavior:

  Before: distribution-spec token endpoint was the default for
          username+password; callers opted into OAuth2 with
          ForceAttemptOAuth2 = true.
  After:  OAuth2 password grant is the default for username+password;
          callers opt into the legacy distribution-spec path with
          client.SetLegacyMode(true).

BREAKING CHANGE: callers that previously relied on the implicit
distribution-spec default (i.e. did not set ForceAttemptOAuth2 and
authenticate via username+password) must now call SetLegacyMode(true) to
preserve the old behavior. Callers that explicitly set
ForceAttemptOAuth2 = true should drop that line (OAuth2 is the new
default). The selection logic itself is unchanged.

Tests are updated accordingly: distribution-spec tests now call
SetLegacyMode(true); OAuth2-forcing tests drop the field. Adds
TestClient_SetLegacyMode for the new setter.

Refs oras-project#1141 (split: PR A of A/B/C).

Signed-off-by: Terry Howe <terrylhowe@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v3 Things belongs to version 3.x

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant