Skip to content

Allow device-code docker login for registries that advertise it #7435

Open

Description

@BRAVO68WEB

Description

Summary

docker login already signs in to Docker Hub in a browser, with a device code. Every other registry only gets a username and password prompt. Let a registry advertise that same flow, and use it when docker login <host> is run without -u or --password.

Motivation

A self-hosted registry that signs people in through a browser (SSO, or its own dashboard) cannot offer that from docker login. The command asks for a password and sends Basic auth to GET /v2/.

The browser flow is limited to Docker Hub:

// cli/command/registry/login.go
if serverAddress == registry.IndexServer && opts.user == "" && opts.password == "" {
    msg, err = loginWithDeviceCodeFlow(ctx, dockerCLI)
}

cli/internal/oauth talks only to login.docker.com. A private registry cannot opt in. The fallback when that flow fails to start (ErrDeviceLoginStartFail) is already the password prompt.

Proposed behavior

  1. docker login registry.example.com, with no username, pings GET /v2/ as it does today.
  2. If the 401 WWW-Authenticate challenge names a device-authorization endpoint, run the existing web login against that endpoint (RFC 8628): print the user code and the verification URL, offer to open the browser, and poll until the user finishes or the code expires.
  3. Store the resulting credential with the existing credential store (a username and secret, or an identity token). Later docker pull and docker push do not change.
  4. If the challenge has no device endpoint, or the flow fails to start, fall back to the username and password prompt.
  5. -u, --password, and --password-stdin keep today's Basic login and skip the browser.

One possible challenge, and the parameter name is open:

WWW-Authenticate: Bearer realm="https://registry.example.com/token",service="registry",device_authorization_endpoint="https://registry.example.com/oauth/device/code"

That endpoint returns the usual device-code JSON (device_code, user_code, verification_uri, interval, expires_in). The token response is a registry credential the CLI can store and present on the next GET /v2/.

What the user sees:

$ docker login registry.example.com
Using web-based login.
To sign in with credentials on the command line, use 'docker login -u <username>'

Your one-time device confirmation code is: ABCD-EFGH
Open this URL to authenticate: https://registry.example.com/activate?user_code=ABCD-EFGH

Press ENTER to open the URL in your browser, or Ctrl+C to cancel.
Waiting for authentication in the browser…
Login Succeeded

Out of scope

Holding GET /v2/ open, or streaming a URL in a partial response body, until the browser finishes. Proxies cut that off, and a client that is not a TTY would hang. Device code is the same "print a link and wait" behavior, with a poll instead of a held connection.

No engine change. The daemon already sends the username and secret the CLI stores.

Alternatives

  • A credential helper per registry. That works, and each registry ships its own binary. This asks the CLI to reuse the flow it already has, so a registry only publishes an endpoint.
  • curl https://registry.example.com/activate | docker login -u token --password-stdin. Possible today. The URL and the secret then share one stdout stream, so it is easy to get the password wrong.

Compatibility

Opt-in. A registry that does not advertise the endpoint behaves as it does now. Docker Hub keeps its current login.

Happy to implement this if the discovery shape looks right.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions