@@ -80,80 +80,137 @@ func verifyloginOptions(dockerCli command.Cli, opts *loginOptions) error {
8080 return nil
8181}
8282
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 {
8584 if err := verifyloginOptions (dockerCli , & opts ); err != nil {
8685 return err
8786 }
8887 var (
8988 serverAddress string
90- response registrytypes.AuthenticateOKBody
89+ response * registrytypes.AuthenticateOKBody
9190 )
9291 if opts .serverAddress != "" && opts .serverAddress != registry .DefaultNamespace {
9392 serverAddress = opts .serverAddress
9493 } else {
9594 serverAddress = registry .IndexServer
9695 }
97-
9896 isDefaultRegistry := serverAddress == registry .IndexServer
97+
98+ // attempt login with current (stored) credentials
9999 authConfig , err := command .GetDefaultAuthConfig (dockerCli .ConfigFile (), opts .user == "" && opts .password == "" , serverAddress , isDefaultRegistry )
100100 if err == nil && authConfig .Username != "" && authConfig .Password != "" {
101- response , err = loginWithCredStoreCreds (ctx , dockerCli , & authConfig )
101+ response , err = loginWithStoredCredentials (ctx , dockerCli , authConfig )
102102 }
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- }
118103
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 )
125108 if err != nil {
126109 return err
127110 }
128111 }
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+
129130 if response .IdentityToken != "" {
130131 authConfig .Password = ""
131132 authConfig .IdentityToken = response .IdentityToken
132133 }
133134
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 )
135195 if err := creds .Store (configtypes .AuthConfig (authConfig )); err != nil {
136196 return errors .Errorf ("Error saving credentials: %v" , err )
137197 }
138198
139- if response .Status != "" {
140- fmt .Fprintln (dockerCli .Out (), response .Status )
141- }
142199 return nil
143200}
144201
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
149209 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
155211 }
156- return response , err
212+
213+ return response , nil
157214}
158215
159216func loginClientSide (ctx context.Context , auth registrytypes.AuthConfig ) (registrytypes.AuthenticateOKBody , error ) {
0 commit comments