@@ -10,6 +10,7 @@ import (
10
10
"github.com/docker/cli/cli/command"
11
11
"github.com/docker/cli/cli/command/completion"
12
12
configtypes "github.com/docker/cli/cli/config/types"
13
+ "github.com/docker/cli/cli/internal/oauth/manager"
13
14
registrytypes "github.com/docker/docker/api/types/registry"
14
15
"github.com/docker/docker/client"
15
16
"github.com/docker/docker/errdefs"
@@ -79,70 +80,145 @@ func verifyloginOptions(dockerCli command.Cli, opts *loginOptions) error {
79
80
return nil
80
81
}
81
82
82
- func runLogin (ctx context.Context , dockerCli command.Cli , opts loginOptions ) error { //nolint:gocyclo
83
- clnt := dockerCli .Client ()
83
+ func runLogin (ctx context.Context , dockerCli command.Cli , opts loginOptions ) error {
84
84
if err := verifyloginOptions (dockerCli , & opts ); err != nil {
85
85
return err
86
86
}
87
87
var (
88
88
serverAddress string
89
- response registrytypes.AuthenticateOKBody
89
+ response * registrytypes.AuthenticateOKBody
90
90
)
91
- if opts .serverAddress != "" && opts .serverAddress != registry .DefaultNamespace {
91
+ if opts .serverAddress != "" &&
92
+ opts .serverAddress != registry .DefaultNamespace &&
93
+ opts .serverAddress != registry .DefaultRegistryHost {
92
94
serverAddress = opts .serverAddress
93
95
} else {
94
96
serverAddress = registry .IndexServer
95
97
}
96
-
97
98
isDefaultRegistry := serverAddress == registry .IndexServer
99
+
100
+ // attempt login with current (stored) credentials
98
101
authConfig , err := command .GetDefaultAuthConfig (dockerCli .ConfigFile (), opts .user == "" && opts .password == "" , serverAddress , isDefaultRegistry )
99
102
if err == nil && authConfig .Username != "" && authConfig .Password != "" {
100
- response , err = loginWithCredStoreCreds (ctx , dockerCli , & authConfig )
103
+ response , err = loginWithStoredCredentials (ctx , dockerCli , authConfig )
101
104
}
105
+
106
+ // if we failed to authenticate with stored credentials (or didn't have stored credentials),
107
+ // prompt the user for new credentials
102
108
if err != nil || authConfig .Username == "" || authConfig .Password == "" {
103
- err = command . ConfigureAuth (ctx , dockerCli , opts . user , opts . password , & authConfig , isDefaultRegistry )
109
+ response , err = loginUser (ctx , dockerCli , opts , authConfig . Username , serverAddress )
104
110
if err != nil {
105
111
return err
106
112
}
113
+ }
114
+
115
+ if response != nil && response .Status != "" {
116
+ _ , _ = fmt .Fprintln (dockerCli .Out (), response .Status )
117
+ }
118
+ return nil
119
+ }
107
120
108
- response , err = clnt .RegistryLogin (ctx , authConfig )
109
- if err != nil && client .IsErrConnectionFailed (err ) {
110
- // If the server isn't responding (yet) attempt to login purely client side
111
- response , err = loginClientSide (ctx , authConfig )
121
+ func loginWithStoredCredentials (ctx context.Context , dockerCli command.Cli , authConfig registrytypes.AuthConfig ) (* registrytypes.AuthenticateOKBody , error ) {
122
+ _ , _ = fmt .Fprintf (dockerCli .Out (), "Authenticating with existing credentials...\n " )
123
+ response , err := dockerCli .Client ().RegistryLogin (ctx , authConfig )
124
+ if err != nil {
125
+ if errdefs .IsUnauthorized (err ) {
126
+ _ , _ = fmt .Fprintf (dockerCli .Err (), "Stored credentials invalid or expired\n " )
127
+ } else {
128
+ _ , _ = fmt .Fprintf (dockerCli .Err (), "Login did not succeed, error: %s\n " , err )
112
129
}
113
- // If we (still) have an error, give up
114
- if err != nil {
115
- return err
130
+ }
131
+
132
+ if response .IdentityToken != "" {
133
+ authConfig .Password = ""
134
+ authConfig .IdentityToken = response .IdentityToken
135
+ }
136
+
137
+ if err := storeCredentials (dockerCli , authConfig ); err != nil {
138
+ return nil , err
139
+ }
140
+
141
+ return & response , err
142
+ }
143
+
144
+ func loginUser (ctx context.Context , dockerCli command.Cli , opts loginOptions , defaultUsername , serverAddress string ) (* registrytypes.AuthenticateOKBody , error ) {
145
+ // If we're logging into the index server and the user didn't provide a username or password, use the device flow
146
+ if serverAddress == registry .IndexServer && opts .user == "" && opts .password == "" {
147
+ response , err := loginWithDeviceCodeFlow (ctx , dockerCli )
148
+ // if the error represents a failure to initiate the device-code flow,
149
+ // then we fallback to regular cli credentials login
150
+ if ! errors .Is (err , manager .ErrDeviceLoginStartFail ) {
151
+ return response , err
116
152
}
153
+ fmt .Fprint (dockerCli .Err (), "Failed to start web-based login - falling back to command line login...\n \n " )
117
154
}
155
+
156
+ return loginWithUsernameAndPassword (ctx , dockerCli , opts , defaultUsername , serverAddress )
157
+ }
158
+
159
+ func loginWithUsernameAndPassword (ctx context.Context , dockerCli command.Cli , opts loginOptions , defaultUsername , serverAddress string ) (* registrytypes.AuthenticateOKBody , error ) {
160
+ // Prompt user for credentials
161
+ authConfig , err := command .PromptUserForCredentials (ctx , dockerCli , opts .user , opts .password , defaultUsername , serverAddress )
162
+ if err != nil {
163
+ return nil , err
164
+ }
165
+
166
+ response , err := loginWithRegistry (ctx , dockerCli , authConfig )
167
+ if err != nil {
168
+ return nil , err
169
+ }
170
+
118
171
if response .IdentityToken != "" {
119
172
authConfig .Password = ""
120
173
authConfig .IdentityToken = response .IdentityToken
121
174
}
175
+ if err = storeCredentials (dockerCli , authConfig ); err != nil {
176
+ return nil , err
177
+ }
178
+
179
+ return & response , nil
180
+ }
181
+
182
+ func loginWithDeviceCodeFlow (ctx context.Context , dockerCli command.Cli ) (* registrytypes.AuthenticateOKBody , error ) {
183
+ store := dockerCli .ConfigFile ().GetCredentialsStore (registry .IndexServer )
184
+ authConfig , err := manager .NewManager (store ).LoginDevice (ctx , dockerCli .Err ())
185
+ if err != nil {
186
+ return nil , err
187
+ }
188
+
189
+ response , err := loginWithRegistry (ctx , dockerCli , registrytypes .AuthConfig (* authConfig ))
190
+ if err != nil {
191
+ return nil , err
192
+ }
193
+
194
+ if err = storeCredentials (dockerCli , registrytypes .AuthConfig (* authConfig )); err != nil {
195
+ return nil , err
196
+ }
122
197
123
- creds := dockerCli .ConfigFile ().GetCredentialsStore (serverAddress )
198
+ return & response , nil
199
+ }
200
+
201
+ func storeCredentials (dockerCli command.Cli , authConfig registrytypes.AuthConfig ) error {
202
+ creds := dockerCli .ConfigFile ().GetCredentialsStore (authConfig .ServerAddress )
124
203
if err := creds .Store (configtypes .AuthConfig (authConfig )); err != nil {
125
204
return errors .Errorf ("Error saving credentials: %v" , err )
126
205
}
127
206
128
- if response .Status != "" {
129
- fmt .Fprintln (dockerCli .Out (), response .Status )
130
- }
131
207
return nil
132
208
}
133
209
134
- func loginWithCredStoreCreds (ctx context.Context , dockerCli command.Cli , authConfig * registrytypes.AuthConfig ) (registrytypes.AuthenticateOKBody , error ) {
135
- fmt .Fprintf (dockerCli .Out (), "Authenticating with existing credentials...\n " )
136
- cliClient := dockerCli .Client ()
137
- response , err := cliClient .RegistryLogin (ctx , * authConfig )
210
+ func loginWithRegistry (ctx context.Context , dockerCli command.Cli , authConfig registrytypes.AuthConfig ) (registrytypes.AuthenticateOKBody , error ) {
211
+ response , err := dockerCli .Client ().RegistryLogin (ctx , authConfig )
212
+ if err != nil && client .IsErrConnectionFailed (err ) {
213
+ // If the server isn't responding (yet) attempt to login purely client side
214
+ response , err = loginClientSide (ctx , authConfig )
215
+ }
216
+ // If we (still) have an error, give up
138
217
if err != nil {
139
- if errdefs .IsUnauthorized (err ) {
140
- fmt .Fprintf (dockerCli .Err (), "Stored credentials invalid or expired\n " )
141
- } else {
142
- fmt .Fprintf (dockerCli .Err (), "Login did not succeed, error: %s\n " , err )
143
- }
218
+ return registrytypes.AuthenticateOKBody {}, err
144
219
}
145
- return response , err
220
+
221
+ return response , nil
146
222
}
147
223
148
224
func loginClientSide (ctx context.Context , auth registrytypes.AuthConfig ) (registrytypes.AuthenticateOKBody , error ) {
0 commit comments