@@ -4,15 +4,20 @@ import (
44 "context"
55 "encoding/base64"
66 "fmt"
7+ "net/http"
78 "os"
89 "strings"
10+ "time"
911
12+ authutil "github.com/containerd/containerd/remotes/docker/auth"
13+ remoteserrors "github.com/containerd/containerd/remotes/errors"
1014 "github.com/depot/cli/pkg/build"
1115 "github.com/docker/cli/cli/config"
1216 "github.com/docker/cli/cli/config/types"
1317 "github.com/moby/buildkit/session"
1418 "github.com/moby/buildkit/session/auth"
1519 "github.com/moby/buildkit/session/auth/authprovider"
20+ "github.com/pkg/errors"
1621 "google.golang.org/grpc"
1722)
1823
@@ -78,9 +83,86 @@ func (a *AuthProvider) Credentials(ctx context.Context, req *auth.CredentialsReq
7883}
7984
8085func (a * AuthProvider ) FetchToken (ctx context.Context , req * auth.FetchTokenRequest ) (* auth.FetchTokenResponse , error ) {
86+ creds , err := a .findCredentials (req .Host )
87+ if err != nil {
88+ return nil , err
89+ }
90+ if creds == nil {
91+ return a .inner .FetchToken (ctx , req )
92+ }
93+
94+ if creds .Password != "" {
95+ return fetchTokenWithFallback (ctx , req , creds )
96+ }
97+
98+ // No secret, fall back to inner provider.
8199 return a .inner .FetchToken (ctx , req )
82100}
83101
102+ // findCredentials looks up decoded credentials for a host from the depot credential list.
103+ func (a * AuthProvider ) findCredentials (host string ) (* types.AuthConfig , error ) {
104+ for _ , c := range a .credentials {
105+ if c .Host != host {
106+ continue
107+ }
108+ decoded , err := base64 .StdEncoding .DecodeString (c .Token )
109+ if err != nil {
110+ return nil , err
111+ }
112+ parts := strings .SplitN (string (decoded ), ":" , 2 )
113+ if len (parts ) != 2 {
114+ return nil , fmt .Errorf ("invalid auth string" )
115+ }
116+ return & types.AuthConfig {
117+ Username : parts [0 ],
118+ 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
149+ }
150+ return nil , err
151+ }
152+ return toFetchTokenResponse (resp .AccessToken , resp .IssuedAt , resp .ExpiresIn ), nil
153+ }
154+
155+ func toFetchTokenResponse (token string , issuedAt time.Time , expires int ) * auth.FetchTokenResponse {
156+ resp := & auth.FetchTokenResponse {
157+ Token : token ,
158+ ExpiresIn : int64 (expires ),
159+ }
160+ if ! issuedAt .IsZero () {
161+ resp .IssuedAt = issuedAt .Unix ()
162+ }
163+ return resp
164+ }
165+
84166func (a * AuthProvider ) GetTokenAuthority (ctx context.Context , req * auth.GetTokenAuthorityRequest ) (* auth.GetTokenAuthorityResponse , error ) {
85167 return a .inner .GetTokenAuthority (ctx , req )
86168}
@@ -124,6 +206,15 @@ func (a *DepotAuthProvider) Credentials(ctx context.Context, req *auth.Credentia
124206}
125207
126208func (a * DepotAuthProvider ) FetchToken (ctx context.Context , req * auth.FetchTokenRequest ) (* auth.FetchTokenResponse , error ) {
209+ creds := GetDepotAuthConfig ()
210+ if creds == nil {
211+ return a .inner .FetchToken (ctx , req )
212+ }
213+
214+ if creds .Password != "" {
215+ return fetchTokenWithFallback (ctx , req , creds )
216+ }
217+
127218 return a .inner .FetchToken (ctx , req )
128219}
129220
0 commit comments