Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ go.work.sum

ralph.sh
plans*
.idea/
4 changes: 2 additions & 2 deletions examples/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,13 @@ require (
go.opentelemetry.io/otel/sdk v1.20.0 // indirect
go.opentelemetry.io/otel/trace v1.20.0 // indirect
go.opentelemetry.io/proto/otlp v0.12.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/oauth2 v0.11.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/term v0.27.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
Expand Down
2 changes: 2 additions & 0 deletions examples/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,7 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
Expand Down Expand Up @@ -790,6 +791,7 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down
1 change: 0 additions & 1 deletion pkg/buildx/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -1010,7 +1010,6 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s

ch, done := progress.NewChannel(pw)
defer func() { <-done }()

cc := c
var printRes map[string][]byte
// DEPOT: stop recording the build steps and traces on the server.
Expand Down
91 changes: 91 additions & 0 deletions pkg/registry/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@ import (
"context"
"encoding/base64"
"fmt"
"net/http"
"os"
"strings"
"time"

authutil "github.com/containerd/containerd/remotes/docker/auth"
remoteserrors "github.com/containerd/containerd/remotes/errors"
"github.com/depot/cli/pkg/build"
"github.com/docker/cli/cli/config"
"github.com/docker/cli/cli/config/types"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/session/auth"
"github.com/moby/buildkit/session/auth/authprovider"
"github.com/pkg/errors"
"google.golang.org/grpc"
)

Expand Down Expand Up @@ -78,9 +83,86 @@ func (a *AuthProvider) Credentials(ctx context.Context, req *auth.CredentialsReq
}

func (a *AuthProvider) FetchToken(ctx context.Context, req *auth.FetchTokenRequest) (*auth.FetchTokenResponse, error) {
creds, err := a.findCredentials(req.Host)
if err != nil {
return nil, err
}
if creds == nil {
return a.inner.FetchToken(ctx, req)
}

if creds.Password != "" {
return fetchTokenWithFallback(ctx, req, creds)
}

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

// findCredentials looks up decoded credentials for a host from the depot credential list.
func (a *AuthProvider) findCredentials(host string) (*types.AuthConfig, error) {
for _, c := range a.credentials {
if c.Host != host {
continue
}
decoded, err := base64.StdEncoding.DecodeString(c.Token)
if err != nil {
return nil, err
}
parts := strings.SplitN(string(decoded), ":", 2)
if len(parts) != 2 {
return nil, fmt.Errorf("invalid auth string")
}
return &types.AuthConfig{
Username: parts[0],
Password: parts[1],
}, nil
}
return nil, nil
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Credential lookup logic duplicated across two methods

Low Severity

The new findCredentials method duplicates the base64-decoding and colon-splitting credential lookup logic already present in the existing Credentials method on AuthProvider. If the credential format or validation ever changes, both places need to be updated in sync. Credentials could be refactored to call findCredentials internally and convert the result, eliminating the duplicated parsing logic.

Additional Locations (1)

Fix in Cursor Fix in Web


// fetchTokenWithFallback attempts OAuth POST first, falling back to GET for registries
// that don't support POST (e.g., GCR returns 404, JFrog returns 401, ACR returns 400).
func fetchTokenWithFallback(ctx context.Context, req *auth.FetchTokenRequest, creds *types.AuthConfig) (*auth.FetchTokenResponse, error) {
to := authutil.TokenOptions{
Realm: req.Realm,
Service: req.Service,
Scopes: req.Scopes,
Username: creds.Username,
Secret: creds.Password,
}

resp, err := authutil.FetchTokenWithOAuth(ctx, http.DefaultClient, nil, "buildkit-client", to)
if err != nil {
var errStatus remoteserrors.ErrUnexpectedStatus
if errors.As(err, &errStatus) {
// Registries without support for POST may return various error codes:
// - GCR: 404
// - JFrog Artifactory: 401
// - ACR: 400
// Fall back to GET for any unexpected status.
getResp, err := authutil.FetchToken(ctx, http.DefaultClient, nil, to)
if err != nil {
return nil, err
}
return toFetchTokenResponse(getResp.Token, getResp.IssuedAt, getResp.ExpiresIn), nil
}
return nil, err
}
return toFetchTokenResponse(resp.AccessToken, resp.IssuedAt, resp.ExpiresIn), nil
}

func toFetchTokenResponse(token string, issuedAt time.Time, expires int) *auth.FetchTokenResponse {
resp := &auth.FetchTokenResponse{
Token: token,
ExpiresIn: int64(expires),
}
if !issuedAt.IsZero() {
resp.IssuedAt = issuedAt.Unix()
}
return resp
}

func (a *AuthProvider) GetTokenAuthority(ctx context.Context, req *auth.GetTokenAuthorityRequest) (*auth.GetTokenAuthorityResponse, error) {
return a.inner.GetTokenAuthority(ctx, req)
}
Expand Down Expand Up @@ -124,6 +206,15 @@ func (a *DepotAuthProvider) Credentials(ctx context.Context, req *auth.Credentia
}

func (a *DepotAuthProvider) FetchToken(ctx context.Context, req *auth.FetchTokenRequest) (*auth.FetchTokenResponse, error) {
creds := GetDepotAuthConfig()
if creds == nil {
return a.inner.FetchToken(ctx, req)
}

if creds.Password != "" {
return fetchTokenWithFallback(ctx, req, creds)
}

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

Expand Down
Loading