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