Skip to content

Commit 337bfa3

Browse files
cursoragentwito
authored andcommitted
Fix duplicated token fetch logic, inconsistent error handling, and missing ACR status code
- Extract shared fetchTokenWithFallback helper function to eliminate code duplication between AuthProvider.FetchToken and DepotAuthProvider.FetchToken - Update findCredentials to return error instead of silently skipping malformed credentials, making error handling consistent with Credentials method - Update OAuth POST fallback to handle all ErrUnexpectedStatus codes, not just 405/404/401, which fixes ACR compatibility (ACR returns 400) Applied via @cursor push command
1 parent e3f8cb0 commit 337bfa3

File tree

1 file changed

+43
-55
lines changed

1 file changed

+43
-55
lines changed

pkg/registry/auth.go

Lines changed: 43 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -83,63 +83,73 @@ func (a *AuthProvider) Credentials(ctx context.Context, req *auth.CredentialsReq
8383
}
8484

8585
func (a *AuthProvider) FetchToken(ctx context.Context, req *auth.FetchTokenRequest) (*auth.FetchTokenResponse, error) {
86-
creds := a.findCredentials(req.Host)
86+
creds, err := a.findCredentials(req.Host)
87+
if err != nil {
88+
return nil, err
89+
}
8790
if creds == nil {
8891
return a.inner.FetchToken(ctx, req)
8992
}
9093

91-
to := authutil.TokenOptions{
92-
Realm: req.Realm,
93-
Service: req.Service,
94-
Scopes: req.Scopes,
95-
Username: creds.Username,
96-
Secret: creds.Password,
97-
}
98-
99-
if to.Secret != "" {
100-
// Credential information is provided, try the OAuth POST endpoint first.
101-
resp, err := authutil.FetchTokenWithOAuth(ctx, http.DefaultClient, nil, "buildkit-client", to)
102-
if err != nil {
103-
var errStatus remoteserrors.ErrUnexpectedStatus
104-
if errors.As(err, &errStatus) {
105-
// Registries without support for POST may return 404 or 401.
106-
if (errStatus.StatusCode == 405 && to.Username != "") || errStatus.StatusCode == 404 || errStatus.StatusCode == 401 {
107-
getResp, err := authutil.FetchToken(ctx, http.DefaultClient, nil, to)
108-
if err != nil {
109-
return nil, err
110-
}
111-
return toFetchTokenResponse(getResp.Token, getResp.IssuedAt, getResp.ExpiresIn), nil
112-
}
113-
}
114-
return nil, err
115-
}
116-
return toFetchTokenResponse(resp.AccessToken, resp.IssuedAt, resp.ExpiresIn), nil
94+
if creds.Password != "" {
95+
return fetchTokenWithFallback(ctx, req, creds)
11796
}
11897

11998
// No secret, fall back to inner provider.
12099
return a.inner.FetchToken(ctx, req)
121100
}
122101

123102
// findCredentials looks up decoded credentials for a host from the depot credential list.
124-
func (a *AuthProvider) findCredentials(host string) *types.AuthConfig {
103+
func (a *AuthProvider) findCredentials(host string) (*types.AuthConfig, error) {
125104
for _, c := range a.credentials {
126105
if c.Host != host {
127106
continue
128107
}
129108
decoded, err := base64.StdEncoding.DecodeString(c.Token)
130109
if err != nil {
131-
continue
110+
return nil, err
132111
}
133112
parts := strings.SplitN(string(decoded), ":", 2)
134113
if len(parts) != 2 {
135-
continue
114+
return nil, fmt.Errorf("invalid auth string")
136115
}
137116
return &types.AuthConfig{
138117
Username: parts[0],
139118
Password: parts[1],
119+
}, nil
120+
}
121+
return nil, nil
122+
}
123+
124+
// fetchTokenWithFallback attempts OAuth POST first, falling back to GET for registries
125+
// that don't support POST (e.g., GCR returns 404, JFrog returns 401, ACR returns 400).
126+
func fetchTokenWithFallback(ctx context.Context, req *auth.FetchTokenRequest, creds *types.AuthConfig) (*auth.FetchTokenResponse, error) {
127+
to := authutil.TokenOptions{
128+
Realm: req.Realm,
129+
Service: req.Service,
130+
Scopes: req.Scopes,
131+
Username: creds.Username,
132+
Secret: creds.Password,
133+
}
134+
135+
resp, err := authutil.FetchTokenWithOAuth(ctx, http.DefaultClient, nil, "buildkit-client", to)
136+
if err != nil {
137+
var errStatus remoteserrors.ErrUnexpectedStatus
138+
if errors.As(err, &errStatus) {
139+
// Registries without support for POST may return various error codes:
140+
// - GCR: 404
141+
// - JFrog Artifactory: 401
142+
// - ACR: 400
143+
// Fall back to GET for any unexpected status.
144+
getResp, err := authutil.FetchToken(ctx, http.DefaultClient, nil, to)
145+
if err != nil {
146+
return nil, err
147+
}
148+
return toFetchTokenResponse(getResp.Token, getResp.IssuedAt, getResp.ExpiresIn), nil
140149
}
150+
return nil, err
141151
}
142-
return nil
152+
return toFetchTokenResponse(resp.AccessToken, resp.IssuedAt, resp.ExpiresIn), nil
143153
}
144154

145155
func toFetchTokenResponse(token string, issuedAt time.Time, expires int) *auth.FetchTokenResponse {
@@ -201,30 +211,8 @@ func (a *DepotAuthProvider) FetchToken(ctx context.Context, req *auth.FetchToken
201211
return a.inner.FetchToken(ctx, req)
202212
}
203213

204-
to := authutil.TokenOptions{
205-
Realm: req.Realm,
206-
Service: req.Service,
207-
Scopes: req.Scopes,
208-
Username: creds.Username,
209-
Secret: creds.Password,
210-
}
211-
212-
if to.Secret != "" {
213-
resp, err := authutil.FetchTokenWithOAuth(ctx, http.DefaultClient, nil, "buildkit-client", to)
214-
if err != nil {
215-
var errStatus remoteserrors.ErrUnexpectedStatus
216-
if errors.As(err, &errStatus) {
217-
if (errStatus.StatusCode == 405 && to.Username != "") || errStatus.StatusCode == 404 || errStatus.StatusCode == 401 {
218-
getResp, err := authutil.FetchToken(ctx, http.DefaultClient, nil, to)
219-
if err != nil {
220-
return nil, err
221-
}
222-
return toFetchTokenResponse(getResp.Token, getResp.IssuedAt, getResp.ExpiresIn), nil
223-
}
224-
}
225-
return nil, err
226-
}
227-
return toFetchTokenResponse(resp.AccessToken, resp.IssuedAt, resp.ExpiresIn), nil
214+
if creds.Password != "" {
215+
return fetchTokenWithFallback(ctx, req, creds)
228216
}
229217

230218
return a.inner.FetchToken(ctx, req)

0 commit comments

Comments
 (0)