Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
43 changes: 38 additions & 5 deletions registry/remote/auth/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,25 @@ type Client struct {
// Reference: https://distribution.github.io/distribution/spec/auth/oauth/#getting-a-token
ClientID string

// ForceAttemptOAuth2 controls whether to follow OAuth2 with password grant
// instead the distribution spec when authenticating using username and
// password.
// legacyMode controls whether to use the legacy distribution spec
// instead of OAuth2 with password grant when authenticating using
// username and password.
// Default is false (OAuth2 is used).
//
// When legacyMode is true, the client uses the legacy distribution spec for
// authentication. If the registry says it supports bearer authentication,
// basic authentication will be performed unless there are no credentials
// or a refresh token is provided. This is the approach used in oras-go
// v1 and v2.
//
// When legacyMode is false (default), the client uses OAuth2 with password
// grant. If the registry says it supports bearer authentication, bearer
// authentication will be performed.
//
// References:
// - https://distribution.github.io/distribution/spec/auth/jwt/
// - https://distribution.github.io/distribution/spec/auth/oauth/
ForceAttemptOAuth2 bool
legacyMode bool
}

// client returns an HTTP client used to access the remote registry.
Expand Down Expand Up @@ -172,6 +184,27 @@ func (c *Client) SetUserAgent(userAgent string) {
c.Header.Set(headerUserAgent, userAgent)
}

// SetLegacyMode sets whether to use legacy distribution spec authentication
// instead of OAuth2 with password grant when authenticating using username
// and password.
//
// When legacy is true, the client uses the legacy distribution spec for
// authentication. If the registry says it supports bearer authentication,
// basic authentication will be performed unless there are no credentials
// or a refresh token is provided. This is the approach used in oras-go
// v1 and v2.
//
// When legacy is false (default), the client uses OAuth2 with password
// grant. If the registry says it supports bearer authentication, bearer
// authentication will be performed.
//
// References:
// - https://distribution.github.io/distribution/spec/auth/jwt/
// - https://distribution.github.io/distribution/spec/auth/oauth/
func (c *Client) SetLegacyMode(legacy bool) {
c.legacyMode = legacy
}

// Do sends the request to the remote server, attempting to resolve
// authentication if 'Authorization' header is not set.
//
Expand Down Expand Up @@ -310,7 +343,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 == EmptyCredential || (cred.RefreshToken == "" && c.legacyMode) {
return c.fetchDistributionToken(ctx, realm, service, scopes, cred.Username, cred.Password)
}
return c.fetchOAuth2Token(ctx, realm, service, scopes, cred)
Expand Down
26 changes: 13 additions & 13 deletions registry/remote/auth/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,7 @@ func TestClient_Do_Bearer_Auth(t *testing.T) {
}, nil
},
}
client.SetLegacyMode(true)

// first request
req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
Expand Down Expand Up @@ -852,6 +853,7 @@ func TestClient_Do_Bearer_Auth_Cached(t *testing.T) {
},
Cache: NewCache(),
}
client.SetLegacyMode(true)

// first request
ctx := WithScopes(context.Background(), scopes...)
Expand Down Expand Up @@ -994,6 +996,7 @@ func TestClient_Do_Bearer_Auth_Cached_PerHost(t *testing.T) {
}),
Cache: NewCache(),
}
client1.SetLegacyMode(true)

// set up server 2
username2 := "test_user2"
Expand Down Expand Up @@ -1064,6 +1067,7 @@ func TestClient_Do_Bearer_Auth_Cached_PerHost(t *testing.T) {
}),
Cache: NewCache(),
}
client2.SetLegacyMode(true)

ctx := context.Background()
ctx = WithScopesForHost(ctx, uri1.Host, scopes1...)
Expand Down Expand Up @@ -1311,7 +1315,6 @@ func TestClient_Do_Bearer_OAuth2_Password(t *testing.T) {
Password: password,
}, nil
},
ForceAttemptOAuth2: true,
}

// first request
Expand Down Expand Up @@ -1458,8 +1461,7 @@ func TestClient_Do_Bearer_OAuth2_Password_Cached(t *testing.T) {
Password: password,
}, nil
},
ForceAttemptOAuth2: true,
Cache: NewCache(),
Cache: NewCache(),
}

// first request
Expand Down Expand Up @@ -1621,8 +1623,7 @@ func TestClient_Do_Bearer_OAuth2_Password_Cached_PerHost(t *testing.T) {
Username: username1,
Password: password1,
}),
ForceAttemptOAuth2: true,
Cache: NewCache(),
Cache: NewCache(),
}
// set up server 2
username2 := "test_user2"
Expand Down Expand Up @@ -1711,8 +1712,7 @@ func TestClient_Do_Bearer_OAuth2_Password_Cached_PerHost(t *testing.T) {
Username: username2,
Password: password2,
}),
ForceAttemptOAuth2: true,
Cache: NewCache(),
Cache: NewCache(),
}

ctx := context.Background()
Expand Down Expand Up @@ -2969,8 +2969,7 @@ func TestClient_Do_Scope_Hint_Mismatch(t *testing.T) {
Password: password,
}, nil
},
ForceAttemptOAuth2: true,
Cache: NewCache(),
Cache: NewCache(),
}

// first request
Expand Down Expand Up @@ -3113,8 +3112,7 @@ func TestClient_Do_Scope_Hint_Mismatch_PerHost(t *testing.T) {
Username: username1,
Password: password1,
}),
ForceAttemptOAuth2: true,
Cache: NewCache(),
Cache: NewCache(),
}

// set up server 1
Expand Down Expand Up @@ -3207,8 +3205,7 @@ func TestClient_Do_Scope_Hint_Mismatch_PerHost(t *testing.T) {
Username: username2,
Password: password2,
}),
ForceAttemptOAuth2: true,
Cache: NewCache(),
Cache: NewCache(),
}

ctx := context.Background()
Expand Down Expand Up @@ -3349,6 +3346,7 @@ func TestClient_Do_Invalid_Credential_Basic(t *testing.T) {
}, nil
},
}
client.SetLegacyMode(true)

// request should fail
req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
Expand Down Expand Up @@ -3434,6 +3432,7 @@ func TestClient_Do_Invalid_Credential_Bearer(t *testing.T) {
}, nil
},
}
client.SetLegacyMode(true)

// request should fail
req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
Expand Down Expand Up @@ -3619,6 +3618,7 @@ func TestClient_Do_Scheme_Change(t *testing.T) {
},
Cache: NewCache(),
}
client.SetLegacyMode(true)

// request with bearer auth
req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
Expand Down
2 changes: 0 additions & 2 deletions registry/remote/auth/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,6 @@ func ExampleClient_Do_clientConfigurations() {
Username: username,
Password: password,
}),
// ForceAttemptOAuth2 controls whether to follow OAuth2 with password grant.
ForceAttemptOAuth2: true,
// Cache caches credentials for accessing the remote registry.
Cache: auth.NewCache(),
}
Expand Down
Loading