Repository navigation
Allow device-code docker login for registries that advertise it #7435
Description
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
docker login registry.example.com, with no username, pingsGET /v2/as it does today.- If the 401
WWW-Authenticatechallenge 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. - Store the resulting credential with the existing credential store (a username and secret, or an identity token). Later
docker pullanddocker pushdo not change. - If the challenge has no device endpoint, or the flow fails to start, fall back to the username and password prompt.
-u,--password, and--password-stdinkeep 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