Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/tutorial/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func main() {
repo.Client = &auth.Client{
Client: retry.DefaultClient,
Cache: auth.NewCache(),
Credential: auth.StaticCredential(repo.Reference.Registry, auth.Credential{
CredentialFunc: auth.StaticCredential(repo.Reference.Registry, credentials.Credential{
Username: "username",
Password: "password",
}),
Expand Down Expand Up @@ -287,7 +287,7 @@ func main() {
repo.Client = &auth.Client{
Client: retry.DefaultClient,
Cache: auth.NewCache(),
Credential: auth.StaticCredential(repo.Reference.Registry, auth.Credential{
CredentialFunc: auth.StaticCredential(repo.Reference.Registry, credentials.Credential{
Username: "username",
Password: "password",
}),
Expand Down
3 changes: 2 additions & 1 deletion example_copy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"github.com/oras-project/oras-go/v3/internal/spec"
"github.com/oras-project/oras-go/v3/registry/remote"
"github.com/oras-project/oras-go/v3/registry/remote/auth"
"github.com/oras-project/oras-go/v3/registry/remote/credentials"
"github.com/oras-project/oras-go/v3/registry/remote/retry"
)

Expand Down Expand Up @@ -451,7 +452,7 @@ func Example_extendedCopyArtifactAndReferrersToRepository() {
repo.Client = &auth.Client{
Client: retry.DefaultClient,
Cache: auth.NewCache(),
Credential: auth.StaticCredential(registry, auth.Credential{
CredentialFunc: credentials.StaticCredentialFunc(registry, credentials.Credential{
Username: "username",
Password: "password",
}),
Expand Down
14 changes: 7 additions & 7 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func Example_pullFilesFromRemoteRepository() {
repo.Client = &auth.Client{
Client: retry.DefaultClient,
Cache: auth.NewCache(),
Credential: auth.StaticCredential(reg, auth.Credential{
CredentialFunc: credentials.StaticCredentialFunc(reg, credentials.Credential{
Username: "username",
Password: "password",
}),
Expand Down Expand Up @@ -85,7 +85,7 @@ func Example_pullImageFromRemoteRepository() {
repo.Client = &auth.Client{
Client: retry.DefaultClient,
Cache: auth.NewCache(),
Credential: auth.StaticCredential(reg, auth.Credential{
CredentialFunc: credentials.StaticCredentialFunc(reg, credentials.Credential{
Username: "username",
Password: "password",
}),
Expand Down Expand Up @@ -125,9 +125,9 @@ func Example_pullImageUsingDockerCredentials() {
panic(err)
}
repo.Client = &auth.Client{
Client: retry.DefaultClient,
Cache: auth.NewCache(),
Credential: credentials.Credential(credStore), // Use the credentials store
Client: retry.DefaultClient,
Cache: auth.NewCache(),
CredentialFunc: remote.GetCredentialFunc(credStore), // Use the credentials store
}

// 2. Copy from the remote repository to the OCI layout store
Expand Down Expand Up @@ -190,7 +190,7 @@ func Example_pushFilesToRemoteRepository() {
repo.Client = &auth.Client{
Client: retry.DefaultClient,
Cache: auth.NewCache(),
Credential: auth.StaticCredential(reg, auth.Credential{
CredentialFunc: credentials.StaticCredentialFunc(reg, credentials.Credential{
Username: "username",
Password: "password",
}),
Expand Down Expand Up @@ -218,7 +218,7 @@ func Example_attachBlobToRemoteRepository() {
repo.Client = &auth.Client{
Client: retry.DefaultClient,
Cache: auth.NewCache(),
Credential: auth.StaticCredential(registry, auth.Credential{
CredentialFunc: credentials.StaticCredentialFunc(registry, credentials.Credential{
Username: "username",
Password: "password",
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package credentials
package remote

import (
"context"
"errors"
"fmt"

"github.com/oras-project/oras-go/v3/registry/remote"
"github.com/oras-project/oras-go/v3/registry/remote/auth"
"github.com/oras-project/oras-go/v3/registry/remote/credentials"
)

// ErrClientTypeUnsupported is thrown by Login() when the registry's client type
Expand All @@ -32,7 +32,7 @@ var ErrClientTypeUnsupported = errors.New("client type not supported")
// registry's client should be nil or of type *auth.Client. Login uses
// a client local to the function and will not modify the original client of
// the registry.
func Login(ctx context.Context, store Store, reg *remote.Registry, cred auth.Credential) error {
func Login(ctx context.Context, store credentials.Store, reg *Registry, cred credentials.Credential) error {
// create a clone of the original registry for login purpose
regClone := *reg
// we use the original client if applicable, otherwise use a default client
Expand All @@ -47,7 +47,7 @@ func Login(ctx context.Context, store Store, reg *remote.Registry, cred auth.Cre
}
regClone.Client = &authClient
// update credentials with the client
authClient.Credential = auth.StaticCredential(reg.Reference.Registry, cred)
authClient.CredentialFunc = credentials.StaticCredentialFunc(reg.Reference.Registry, cred)
// validate and store the credential
if err := regClone.Ping(ctx); err != nil {
return fmt.Errorf("failed to validate the credentials for %s: %w", regClone.Reference.Registry, err)
Expand All @@ -60,20 +60,20 @@ func Login(ctx context.Context, store Store, reg *remote.Registry, cred auth.Cre
}

// Logout provides the logout functionality given the registry name.
func Logout(ctx context.Context, store Store, registryName string) error {
func Logout(ctx context.Context, store credentials.Store, registryName string) error {
registryName = ServerAddressFromRegistry(registryName)
if err := store.Delete(ctx, registryName); err != nil {
return fmt.Errorf("failed to delete the credential for %s: %w", registryName, err)
}
return nil
}

// Credential returns a Credential() function that can be used by auth.Client.
func Credential(store Store) auth.CredentialFunc {
return func(ctx context.Context, hostport string) (auth.Credential, error) {
// GetCredentialFunc returns a GetCredentialFunc() function that can be used by auth.Client.
func GetCredentialFunc(store credentials.Store) credentials.CredentialFunc {
return func(ctx context.Context, hostport string) (credentials.Credential, error) {
hostport = ServerAddressFromHostname(hostport)
if hostport == "" {
return auth.EmptyCredential, nil
return credentials.EmptyCredential, nil
}
return store.Get(ctx, hostport)
}
Expand Down
42 changes: 10 additions & 32 deletions registry/remote/auth/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"net/url"
"strings"

"github.com/oras-project/oras-go/v3/registry/remote/credentials"
"github.com/oras-project/oras-go/v3/registry/remote/internal/errutil"
"github.com/oras-project/oras-go/v3/registry/remote/retry"
)
Expand Down Expand Up @@ -66,29 +67,6 @@ var maxResponseBytes int64 = 128 * 1024 // 128 KiB
// See also ClientID.
var defaultClientID = "oras-go"

// CredentialFunc represents a function that resolves the credential for the
// given registry (i.e. host:port).
//
// [EmptyCredential] is a valid return value and should not be considered as
// an error.
type CredentialFunc func(ctx context.Context, hostport string) (Credential, error)

// StaticCredential specifies static credentials for the given host.
func StaticCredential(registry string, cred Credential) CredentialFunc {
if registry == "docker.io" {
// it is expected that traffic targeting "docker.io" will be redirected
// to "registry-1.docker.io"
// reference: https://github.com/moby/moby/blob/v24.0.0-beta.2/registry/config.go#L25-L48
registry = "registry-1.docker.io"
}
return func(_ context.Context, hostport string) (Credential, error) {
if hostport == registry {
return cred, nil
}
return EmptyCredential, nil
}
}

// Client is an auth-decorated HTTP client.
// Its zero value is a usable client that uses http.DefaultClient with no cache.
type Client struct {
Expand All @@ -105,12 +83,12 @@ type Client struct {
// Header contains the custom headers to be added to each request.
Header http.Header

// Credential specifies the function for resolving the credential for the
// CredentialFunc specifies the function for resolving the credential for the
// given registry (i.e. host:port).
// EmptyCredential is a valid return value and should not be considered as
// an error.
// If nil, the credential is always resolved to EmptyCredential.
Credential CredentialFunc
CredentialFunc credentials.CredentialFunc

// Cache caches credentials for direct accessing the remote registry.
// If nil, no cache is used.
Expand Down Expand Up @@ -148,11 +126,11 @@ func (c *Client) send(req *http.Request) (*http.Response, error) {
}

// credential resolves the credential for the given registry.
func (c *Client) credential(ctx context.Context, reg string) (Credential, error) {
if c.Credential == nil {
return EmptyCredential, nil
func (c *Client) credential(ctx context.Context, reg string) (credentials.Credential, error) {
if c.CredentialFunc == nil {
return credentials.EmptyCredential, nil
}
return c.Credential(ctx, reg)
return c.CredentialFunc(ctx, reg)
}

// cache resolves the cache.
Expand Down Expand Up @@ -291,7 +269,7 @@ func (c *Client) fetchBasicAuth(ctx context.Context, registry string) (string, e
if err != nil {
return "", fmt.Errorf("failed to resolve credential: %w", err)
}
if cred == EmptyCredential {
if cred == credentials.EmptyCredential {
return "", ErrBasicCredentialNotFound
}
if cred.Username == "" || cred.Password == "" {
Expand All @@ -310,7 +288,7 @@ func (c *Client) fetchBearerToken(ctx context.Context, registry, realm, service
if cred.AccessToken != "" {
return cred.AccessToken, nil
}
if cred == EmptyCredential || (cred.RefreshToken == "" && !c.ForceAttemptOAuth2) {
if cred == credentials.EmptyCredential || (cred.RefreshToken == "" && !c.ForceAttemptOAuth2) {
return c.fetchDistributionToken(ctx, realm, service, scopes, cred.Username, cred.Password)
}
return c.fetchOAuth2Token(ctx, realm, service, scopes, cred)
Expand Down Expand Up @@ -370,7 +348,7 @@ func (c *Client) fetchDistributionToken(ctx context.Context, realm, service stri

// fetchOAuth2Token fetches an OAuth2 access token.
// Reference: https://distribution.github.io/distribution/spec/auth/oauth/
func (c *Client) fetchOAuth2Token(ctx context.Context, realm, service string, scopes []string, cred Credential) (string, error) {
func (c *Client) fetchOAuth2Token(ctx context.Context, realm, service string, scopes []string, cred credentials.Credential) (string, error) {
form := url.Values{}
if cred.RefreshToken != "" {
form.Set("grant_type", "refresh_token")
Expand Down
Loading
Loading