April 11, 2023·4 min read·engineeringrubyrubygemssecuritywebauthn

RubyGems brings browser-based security devices to CLI authentication

RubyGems 3.4.12 adds experimental WebAuthn support to CLI authentication through a browser-assisted verification flow, while account-level recovery work remains unfinished.

A maintainer's browser login and command-line publishing workflow can have different authentication capabilities. RubyGems added security devices to the website in December, but the CLI could not initially use them when signing in. That gap mattered because command-line access is part of how a maintainer obtains credentials and performs protected package operations.

The April 11 CLI support announcement extends registered security devices to RubyGems commands. The feature is available in RubyGems 3.4.12 and later. Its accompanying release notes label the new CLI support experimental.

I appreciate Ruby's readable, consistent programming model, but a package ecosystem also depends on who can publish the code an application installs. Authentication work is consequential even though it does not change the syntax or runtime behavior of that code.

Browser registration preceded CLI support

The December security-device announcement introduced hardware tokens and passkeys for RubyGems.org accounts. Registration happens through the account settings page under Security Device.

At that point, the CLI did not support those devices during sign-in. The announcement suggested generating an API key in the web interface as a workaround. A device could protect the browser interaction without being directly available in the command-line authentication flow.

April closes that particular workflow gap. A user with a registered device can authenticate through the browser when the CLI requests the additional factor. The same process can apply to other MFA-required commands when the relevant protection is enabled.

That last condition matters. Registering a device, authenticating a sign-in, and requiring MFA for a protected operation are related choices. The announcement does not claim that every CLI command now requires a browser interaction.

The terminal delegates the device interaction

The RubyGems 3.4.12 implementation makes the bridge concrete. When an API request receives the expected MFA-required response, the client obtains an additional authentication code and retries the request.

The code first asks for a WebAuthn verification URL. It can authenticate that request using the API key or the sign-in credentials, depending on the operation. If a verification URL is returned, the CLI enters its device-verification path.

That path starts a TCP listener on an automatically assigned port. The displayed verification URL includes that port, and the CLI tells the user to visit it to authenticate with a security device. A listener thread waits for the resulting code while the command waits for verification.

This is browser-assisted CLI authentication, not a terminal implementation of the browser's security-device interface. The browser handles the device interaction, and the command receives the result needed to continue the protected API request.

The implementation's message is also more precise than assuming a browser always opens automatically. It tells the user to visit a URL. The command-line flow therefore needs a usable browser path even though the original operation began in the terminal.

Verification fits the existing request flow

The resulting code becomes the OTP value supplied on the retried API request. An existing request helper places that value in the request header. The surrounding command does not need to implement a separate version of its package operation merely because the user chose a security device.

This is an integration with the existing additional-factor mechanism. The source retains a path that asks for a conventional OTP when no WebAuthn verification URL is available. It also tells users with OTP enabled how to rerun sign-in with the OTP option if they cannot complete device verification.

Those paths explain the release's scope. Device support is another way to satisfy the authentication step, while the API operation and its required permissions remain relevant. Successful verification does not itself grant every possible API-key scope.

The same utilities still contain scope selection and scope-update behavior. Authentication proves the required identity or factor for a request, and authorization determines what the key can do. Keeping those questions distinct helps explain why an authenticated operation can still require a scope update.

Hardware tokens and passkeys have different recovery considerations

The December announcement describes hardware tokens as physical devices holding key material and recommends a backup token because a device can be lost. It also describes passkeys as using an existing device's protected storage, with synchronization available across devices in supporting environments.

These options give maintainers different ways to make a registered security device available. They also create different dependencies on the devices and browser environment used to authenticate. The earlier announcement explicitly notes that device and browser support varies.

This is why I see the browser-assisted approach as a practical extension. It reuses the environment where the device is already registered and supported, while allowing the CLI operation to proceed through a defined verification step.

The April post is careful about what remains incomplete. WebAuthn is not yet a full replacement for time-based one-time passwords in RubyGems account configuration. Recovery codes for security-device users and selection of MFA levels without setting up TOTP are described as future work.

Experimental support is a release property

RubyGems 3.4.12's notes identify WebAuthn CLI support as the release's experimental enhancement. They also state that Bundler 2.4.12 is installed as a default gem. The authentication feature belongs to RubyGems, even when Bundler is present in the same tool installation.

The notes provide the usual command for updating the installed RubyGems system.

gem update --system

A maintainer also needs a registered device for the browser-assisted path. Updating the CLI supplies the client implementation, while registration supplies the account-side device association. Neither step can stand in for the other.

The change makes an existing website authentication method usable from the CLI's protected-request flow. Its immediate effect is specific: a supported RubyGems client can request device verification, direct the user to the browser, receive the verification result, and retry the operation. Account recovery and complete independence from TOTP remain separate work in the April announcement.