August 27, 2024·4 min read·engineeringdockerauthenticationclioauth

Docker CLI device login moves interactive authentication to the browser

Docker 27.2 adds device-code login for Docker Hub. The CLI still manages registry credentials, but browser authentication changes how an interactive session obtains them.

Logging in to a registry combines two tasks that do not have to happen in the same interface. A person proves their identity, and the client obtains credentials it can use for registry requests. Docker's new device-code flow moves the first task into a browser while retaining the second in the CLI's credential machinery.

Docker 27.2.0, released on August 27, adds this flow for Docker Hub. The release notes preserve username-based login as an alternative. The distinction is between an interactive authentication path and the credentials subsequently used by registry operations, rather than between authenticated and unauthenticated image access.

I prefer Docker workflows whose dependencies remain explicit. Authentication is one of those dependencies even when the application artifact is repeatable. The useful question here is how a browser-confirmed identity becomes a credential that the client can store and use after the browser interaction ends.

The default path depends on existing credentials

The version-tagged login implementation first determines the registry address and looks for stored credentials. When usable stored credentials are available, it attempts authentication with them. The new flow therefore does not imply that every invocation of docker login must begin another browser exchange.

When new credentials are needed, the device flow is selected for Docker Hub if the caller has supplied neither a username nor a password. The code also provides an environment-controlled escape from that selection. Other registry addresses continue through the username and password path.

The ordinary interactive entry point remains the login command without credential arguments. For Docker Hub, it can initiate the device flow when new credentials are needed:

docker login

Supplying a username selects the other documented path. The client can then obtain the remaining credential through its command-line interaction:

docker login --username example-user

These commands describe different ways to obtain new credentials when the existing configuration does not already satisfy login. They are not requests to change which image a later pull selects. Login establishes registry access independently of the image's platform or build configuration.

The device code connects the CLI to browser confirmation

The OAuth manager in this release begins by requesting a device code. If the initial response lacks a user code, it treats startup as a failure. A successful response gives the CLI a one-time confirmation code and a verification address to display.

The implementation then waits for the authentication result while offering to open the verification address when the user presses Enter. It also prints an address where the user can submit the code. The CLI is therefore not dependent on the browser-opening helper succeeding before it can describe the confirmation step.

Waiting for the result has explicit cancellation and error paths. Cancellation returns a login-canceled error. A failure while waiting for authentication is reported separately from an inability to start the device flow. Those states matter because the username fallback applies specifically to startup failure.

In the login command, failure to initiate device login falls back to command-line credential entry. Other results return through their own paths. Treating every failed browser login as a request to prompt for another secret would hide the difference between an unavailable initiation service and an authentication exchange that has already begun.

Browser completion still produces stored credentials

After authentication, the manager parses the access token's claims and stores access and refresh tokens through the configured credential store. The release implementation uses separate store keys for those tokens. It then requests an automatically created Hub personal access token and returns a registry authentication configuration containing that credential.

The login command authenticates that configuration with the registry and stores it through the existing credential machinery. This is why avoiding password entry at the terminal should not be described as eliminating client credentials. The process obtains tokens and persists the credentials needed for subsequent operations.

The login documentation shipped with 27.2.0 explains credential stores and registry-specific helpers. An external helper can place credentials in an operating-system store. Without an applicable helper, the documented fallback stores encoded credentials in the client configuration file.

That storage policy remains relevant after changing the interactive login interface. A browser confirmation changes acquisition of credentials. The configured store determines where the acquired material resides and how later commands retrieve it. The login source routes both paths through that storage responsibility.

My preference is to keep acquisition and storage separate when describing this feature. The browser can provide a more suitable interface for a person's authentication, while the CLI still needs a deliberate credential-store configuration. Neither part establishes the behavior of the other automatically.

Automation retains an explicit input path

The versioned documentation retains --password-stdin for noninteractive login. Its option validation requires a username when that flag is used and rejects combining it with --password. Reading the credential from standard input avoids placing its literal value in the command's argument list.

That is a different workflow from waiting for a person to complete browser authentication. A pipeline that supplies a username and token continues to express those inputs explicitly. The device-code addition does not require a browser-driven interaction for every supported login use case.

The scope is also Docker Hub. The implementation checks the default registry before selecting the device flow. It does not announce a generic device authorization protocol for arbitrary private registries, whose authentication services and supported client interactions may differ.

Image listings gain another kind of visibility

The same release adds experimental docker image ls --tree support with the containerd image store. Its purpose is to show whether an image has multiple platforms. The image-list API can also include a Manifests field when the request explicitly enables the manifests query option.

Those entries can describe platform manifests and build attestations. This improves visibility into the image index, while device login changes how the CLI obtains registry access. The two additions affect different questions in an image workflow. One establishes client credentials, and the other exposes more of the stored artifact's structure through a requested experimental response.